關(guān)于JavaScript反調(diào)試與混淆識(shí)別舉例詳解
一、常見反調(diào)試手段識(shí)別
1. debugger 死循環(huán)(阻塞調(diào)試器)
樣例代碼:
while (true) {
debugger;
}
原理:
每次執(zhí)行到
debugger語句,如果 DevTools 打開,將自動(dòng)觸發(fā)斷點(diǎn)。如果在死循環(huán)中,調(diào)試器會(huì)被頻繁打斷,卡死頁面或無法操作。
特征識(shí)別:
出現(xiàn)在自執(zhí)行函數(shù)中
(function(){...})(),或者 setInterval、setTimeout 里。常與加密代碼混合,隱藏在核心邏輯附近。
應(yīng)對(duì)方法:
打開 Chrome DevTools → 設(shè)置 → 關(guān)閉 "暫停于異常" 和自動(dòng)斷點(diǎn)。
手動(dòng)刪除
debugger,或用 Babel/AST 腳本批量清除:
traverse(ast, {
DebuggerStatement(path) {
path.remove();
}
});
2. 時(shí)間延遲檢測(cè)(檢測(cè)調(diào)試耗時(shí))
樣例代碼:
const start = Date.now();
for (let i = 0; i < 1e8; i++) {}
const end = Date.now();
if (end - start > 500) {
alert("你調(diào)試我了!");
}
原理:
調(diào)試器中手動(dòng)打斷點(diǎn),會(huì)暫停腳本執(zhí)行。
利用
Date.now()或performance.now()計(jì)算耗時(shí),如果超過閾值,說明你在調(diào)試。
特征識(shí)別:
Date.now()、performance.now()出現(xiàn)多次用于比對(duì)時(shí)間差。常用于函數(shù)入口、加密函數(shù)前后。
應(yīng)對(duì)方法:
重寫時(shí)間函數(shù),返回恒定時(shí)間:
Date.now = () => 123456789; performance.now = () => 100;
- 或刪除時(shí)間差判斷代碼段(使用 AST 或腳本)
3. 函數(shù)串?dāng)_檢測(cè)(檢測(cè)函數(shù)是否被 hook)
樣例代碼:
const realAlert = alert;
alert = function () {
console.log("你調(diào)試了嗎?");
return realAlert.apply(this, arguments);
};
原理:
檢測(cè)是否篡改了系統(tǒng)函數(shù),例如 alert、console、eval 等。
調(diào)試器經(jīng)常 hook 函數(shù),攻擊者利用此邏輯檢測(cè)調(diào)試行為。
特征識(shí)別:
檢測(cè)
alert,eval,console.log等函數(shù)是否被改寫。Function.prototype.toString()經(jīng)常聯(lián)合使用,判斷函數(shù)體內(nèi)容是否[native code]
應(yīng)對(duì)方法:
恢復(fù)原始函數(shù):
alert = window.__proto__.alert; console.log = window.__proto__.console.log;
- 用 Proxy 包裝函數(shù),同時(shí)偽裝
toString()輸出。
4. Function.prototype.toString 檢測(cè)(偽裝函數(shù)原型)
樣例代碼:
if (alert.toString().indexOf('[native code]') === -1) {
throw new Error('你修改了 alert 函數(shù)!');
}
原理:
JS 中原生函數(shù)
toString()會(huì)返回[native code]。一旦你對(duì)
alert之類做了 hook 或包裝,這一特征就消失。
反制手段:
偽裝 toString 輸出:
alert = new Proxy(alert, {
apply: function(target, thisArg, argumentsList) {
return target.apply(thisArg, argumentsList);
}
});
alert.toString = function() {
return "function alert() { [native code] }";
};
5. window.console 檢測(cè)
樣例代碼:
if (!window.console || typeof console.log !== "function") {
throw new Error("console 被篡改!");
}
原理:
某些調(diào)試工具會(huì)臨時(shí)修改 console 行為。
檢測(cè) console 存在性和完整性也是一種反調(diào)試手段。
解決方式:
恢復(fù)原始 console 對(duì)象。
或偽造 console 對(duì)象結(jié)構(gòu)(手動(dòng)定義全套方法)。
6. 異常捕獲干擾調(diào)試
樣例代碼:
try {
throw new Error("調(diào)試干擾");
} catch (e) {
debugger;
}
原理:
在 try-catch 中故意嵌入 debugger、死循環(huán)或跳轉(zhuǎn)邏輯,擾亂調(diào)試節(jié)奏。
有的框架會(huì)在異常棧中判斷調(diào)試器是否存在。
應(yīng)對(duì)方案:
修改 try-catch 中間代碼,繞開 debugger。
用 AST 替換整個(gè)異常處理段。
7. 堆棧跟蹤干擾(檢測(cè)調(diào)試器存在)
樣例代碼:
function checkStack() {
try {
throw new Error();
} catch (e) {
if (e.stack.indexOf("Debugger") !== -1) {
alert("發(fā)現(xiàn)調(diào)試器");
}
}
}
原理:
拋異常時(shí),
Error.stack會(huì)包含調(diào)用堆棧信息。如果你在 Chrome DevTools 設(shè)置了斷點(diǎn),可能會(huì)在 stack 中體現(xiàn)。
應(yīng)對(duì):
重寫 Error 構(gòu)造函數(shù)或其 stack 屬性,返回空堆棧。
8. requestAnimationFrame 反調(diào)試
樣例代碼:
let lastTime = performance.now();
function checkDebugger() {
let now = performance.now();
if (now - lastTime > 100) {
alert("調(diào)試器卡住了瀏覽器");
}
lastTime = now;
requestAnimationFrame(checkDebugger);
}
requestAnimationFrame(checkDebugger);
原理:
requestAnimationFrame頻率非常高(約每16ms執(zhí)行一次),調(diào)試器會(huì)明顯卡頓,時(shí)間差變大。
應(yīng)對(duì)方法:
重寫
requestAnimationFrame,屏蔽檢測(cè)邏輯。或 hook
performance.now()偽造時(shí)間。
9. 全局函數(shù)自毀 / 還原干擾
樣例代碼:
(function(){
Function = null;
})();
原理:
故意銷毀 JS 全局函數(shù),阻止我們運(yùn)行 eval / Function 等調(diào)試代碼。
應(yīng)對(duì)方法:
在中斷點(diǎn)處提前
window.Function = Function備份或直接改寫函數(shù)覆蓋邏輯,讓其失效
總結(jié)
| 技術(shù)名 | 檢測(cè)方式 | 應(yīng)對(duì)手段 |
|---|---|---|
debugger 死循環(huán) | debugger 頻繁觸發(fā) | AST 移除 / DevTools 設(shè)置 |
| 時(shí)間延遲檢測(cè) | Date.now() / performance.now() | Hook 時(shí)間函數(shù) / 刪除判斷 |
| 函數(shù)串?dāng)_ | 檢查 alert、console、eval 是否被 hook | 還原函數(shù) / Proxy + toString 偽裝 |
toString 檢測(cè) | 判斷函數(shù)是否為 [native code] | 改寫 toString 方法 |
| console 檢測(cè) | 是否存在 console 方法 | 偽造完整 console 對(duì)象結(jié)構(gòu) |
| 異常捕獲干擾 | try-catch 嵌入 debugger | 替換整個(gè)代碼塊 |
| 堆棧跟蹤調(diào)試檢測(cè) | 分析 Error.stack 內(nèi)容 | 重寫 Error / 清空 stack |
| requestAnimationFrame 檢測(cè) | 檢測(cè)瀏覽器執(zhí)行頻率 | 重寫 rAF / 時(shí)間偽造 |
二、JavaScript 混淆手法
混淆的目標(biāo)是讓代碼變得:
難讀(降低可讀性)
難調(diào)試(隱藏執(zhí)行流程)
難還原(阻礙逆向)
1. 字符串拆分拼接
原始代碼:
var key = "secretKey";
混淆后代碼示例:
var _0x1a2b = "sec" + "ret" + "Key";
甚至更復(fù)雜:
var a = "s"; var b = "e"; var c = b + "c"; var d = a + c + "ret" + "Key"; // 最終是 "secretKey"
混淆目的:
隱藏字符串字面量,防止關(guān)鍵詞命中(如 w、sign、token 等)。
識(shí)別與還原方法:
手動(dòng)打斷點(diǎn)查看變量值(或用 console.log 打?。?/p>
或者用 AST 腳本靜態(tài)還原拼接結(jié)果(字符串折疊優(yōu)化):
traverse(ast, {
BinaryExpression(path) {
if (path.node.operator === "+" &&
t.isStringLiteral(path.node.left) &&
t.isStringLiteral(path.node.right)) {
path.replaceWith(
t.stringLiteral(path.node.left.value + path.node.right.value)
);
}
}
});
2. base64 編碼隱藏字符串
混淆代碼示例:
var data = atob("c2VjcmV0S2V5"); // "secretKey"
原理:
用 Base64 編碼字符串后解碼執(zhí)行,達(dá)到隱藏真實(shí)內(nèi)容的目的。
通常與
eval,Function,new Image()等組合使用。
混淆目的:
防止明文暴露關(guān)鍵字(如 UA、token、cookie、sign 等)
識(shí)別特征:
atob()、btoa()、Buffer.from(...).toString(...)出現(xiàn)明文字符串為 4 的倍數(shù)長(zhǎng)度,末尾常有
=填充符
還原方式:
瀏覽器或腳本執(zhí)行:
console.log(atob("c2VjcmV0S2V5"));
3. eval / Function 動(dòng)態(tài)執(zhí)行
示例 1(eval):
eval("console." + "log('hello')");
示例 2(Function):
var code = "return 5 + 5;"; var fn = new Function(code); console.log(fn()); // 輸出 10
原理:
動(dòng)態(tài)執(zhí)行字符串拼接后的代碼,防止靜態(tài)分析。
通常與字符串拼接、base64 一起使用。
特征識(shí)別:
出現(xiàn)
eval(),new Function(),setTimeout(code, 0)等動(dòng)態(tài)執(zhí)行語句。動(dòng)態(tài)生成的代碼中常含混淆函數(shù)調(diào)用、加密入口、hook 代碼。
還原與處理:
1)打補(bǔ)丁替換 eval 為 console.log:
eval = console.log; // 打印出真實(shí)代碼
2)攔截 Function:
window.Function = function(code) {
console.log("[HOOKED FUNCTION]:", code);
return () => {};
};
3)使用 AST 替換 eval:
traverse(ast, {
CallExpression(path) {
if (path.node.callee.name === 'eval') {
path.node.callee.name = 'console.log';
}
}
});
4. 數(shù)組 + 索引跳轉(zhuǎn)(Control Flow Flattening)
混淆代碼示例:
var _0xabc = [
"log", // 索引 0
"Hello", // 索引 1
"console", // 索引 2
];
(function(arr) {
var a = arr[2]; // "console"
var b = arr[0]; // "log"
var c = arr[1]; // "Hello"
window[a][b](c); // => console.log("Hello")
})(_0xabc);
或者極端一點(diǎn):
var arr = ["\x63\x6f\x6e\x73\x6f\x6c\x65", "\x6c\x6f\x67"];
window[arr[0]][arr[1]]("hi");
原理:
字符串存入數(shù)組,索引讀取,打亂順序。
控制流 flatten:真實(shí)執(zhí)行路徑隱藏在數(shù)組索引組合里。
特征識(shí)別:
有大數(shù)組存放字符串
數(shù)組通過索引訪問,變量命名毫無意義(如
_0xabc[1])出現(xiàn) “mapping 函數(shù)”:例如
_0xabc = function(i){ return arr[i]; }
還原方法:
手動(dòng)記錄數(shù)組內(nèi)容 → 替換索引值
使用 Babel AST 掃描,把數(shù)組取值還原成字符串
工具推薦:
de4js:https://lelinhtinh.github.io/de4js/自寫還原腳本處理全局映射數(shù)組
總結(jié)
| 混淆類型 | 特征 | 應(yīng)對(duì)手段 |
|---|---|---|
| 字符串拼接 | 多個(gè)字符串拼成關(guān)鍵字 | AST 靜態(tài)還原 / 打斷點(diǎn)查看 |
| Base64 編碼 | 出現(xiàn) atob(), 字符串有 = | 解碼查看 / Python 輔助 |
| 動(dòng)態(tài)執(zhí)行 | eval, Function, setTimeout | Hook 動(dòng)態(tài)函數(shù) / AST 替換打印 |
| 數(shù)組+索引跳轉(zhuǎn) | 大數(shù)組 + 隨機(jī)索引訪問 | 還原數(shù)組映射 / 替換所有訪問語句 |
三、AST 抽象語法樹分析
AST(抽象語法樹) 是程序源代碼的結(jié)構(gòu)化、樹狀表示。
在 JavaScript 中,一段代碼:
var a = "hello";
會(huì)被轉(zhuǎn)換為一個(gè) AST 樹結(jié)構(gòu),描述這段代碼的結(jié)構(gòu),比如:VariableDeclaration -> VariableDeclarator -> Identifier + Literal
它并不是運(yùn)行代碼,而是「代碼結(jié)構(gòu)本身」的抽象。
在 JS 混淆還原、定位加密函數(shù)、批量清理垃圾邏輯時(shí),AST 是最強(qiáng)的靜態(tài)分析工具:
| 任務(wù) | AST 作用 |
|---|---|
| 還原混淆(字符串拼接、數(shù)組索引) | 靜態(tài)提取還原拼接結(jié)果 |
| 刪除垃圾代碼(無用判斷等) | 刪除某些結(jié)構(gòu)的語句(如 if (false)) |
| 替換函數(shù)調(diào)用 | 將 eval() 改為 console.log() 等 |
| 查找加密入口、核心參數(shù)生成 | 定位函數(shù)名和依賴鏈,追蹤代碼調(diào)用路徑 |
Babel 是 JS 編譯領(lǐng)域的核心工具,它能:
解析 JS 源碼為 AST(
@babel/parser)遍歷和修改 AST(
@babel/traverse)將 AST 重新生成代碼(
@babel/generator)
1. Babel AST 操作的基本流程
安裝依賴(Node 環(huán)境)
npm install @babel/parser @babel/traverse @babel/generator
1)將代碼解析成 AST
const parser = require('@babel/parser');
const code = 'var a = "he" + "llo";';
const ast = parser.parse(code, {
sourceType: 'module'
});
2)遍歷 AST 并修改
const traverse = require('@babel/traverse').default;
const t = require('@babel/types');
traverse(ast, {
BinaryExpression(path) {
if (
path.node.operator === '+' &&
t.isStringLiteral(path.node.left) &&
t.isStringLiteral(path.node.right)
) {
// 替換拼接表達(dá)式為結(jié)果字符串
path.replaceWith(
t.stringLiteral(path.node.left.value + path.node.right.value)
);
}
}
});
3)生成新代碼
const generate = require('@babel/generator').default;
const output = generate(ast);
console.log(output.code); // var a = "hello";
2. 實(shí)戰(zhàn):還原混淆數(shù)組+索引跳轉(zhuǎn)代碼
示例混淆代碼:
var _0xabc = ["se", "cret", "Key"]; var key = _0xabc[0] + _0xabc[1] + _0xabc[2];
還原目標(biāo):把 _0xabc[0] 直接替換成 "se" 等,變成:
var key = "secretKey";
解法思路:
找出數(shù)組聲明內(nèi)容,建立索引映射
遍歷代碼中所有的
MemberExpression(屬性訪問)如果是
_0xabc[0],直接替換成"se"的字符串字面量
Babel 腳本:
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generate = require('@babel/generator').default;
const t = require('@babel/types');
const code = `
var _0xabc = ["se", "cret", "Key"];
var key = _0xabc[0] + _0xabc[1] + _0xabc[2];
`;
const ast = parser.parse(code);
let mapping = {};
traverse(ast, {
VariableDeclarator(path) {
if (
t.isIdentifier(path.node.id) &&
t.isArrayExpression(path.node.init)
) {
const arrName = path.node.id.name;
const elements = path.node.init.elements;
mapping[arrName] = elements.map(e => e.value);
}
},
MemberExpression(path) {
const obj = path.node.object;
const prop = path.node.property;
if (
t.isIdentifier(obj) &&
mapping[obj.name] &&
t.isNumericLiteral(prop)
) {
const value = mapping[obj.name][prop.value];
path.replaceWith(t.stringLiteral(value));
}
}
});
const output = generate(ast);
console.log(output.code);
3. 輔助工具推薦
| 工具 | 說明 |
|---|---|
| AST Explorer | 可視化查看 AST 結(jié)構(gòu),非常適合新手理解 |
| Babel + Node 腳本 | 實(shí)際靜態(tài)還原代碼 |
| Chrome DevTools | 配合調(diào)試、打斷點(diǎn)驗(yàn)證邏輯是否還原成功 |
總結(jié)
AST 是在面對(duì) JS 混淆與參數(shù)還原時(shí),最強(qiáng)的“靜態(tài)分析武器”,一旦掌握,就能自動(dòng)還原大量加密、反調(diào)試邏輯,讓 JS 逆向效率質(zhì)變!
四、定位核心參數(shù)生成函數(shù)
在爬蟲、逆向場(chǎng)景中,服務(wù)端通常要求提交一些“加密參數(shù)”:
常見名稱有:
w、sign、token、auth、xyz、m、h等這些參數(shù)通過 JS 中隱藏/混淆的函數(shù)生成,是反爬的關(guān)鍵一環(huán)
我們的任務(wù)是定位并還原這些函數(shù)!
1. 典型例子
例如某請(qǐng)求:
POST /api/check headers: w: "e72fa5b18d320...(加密值)"
需要搞清楚:
誰生成了
w?w用了哪些參數(shù)(時(shí)間戳、cookie、UA、行為數(shù)據(jù))?生成函數(shù)是否被混淆?
是否用了動(dòng)態(tài)執(zhí)行(eval、Function)?
是否跑在 WebWorker 或 iframe 中?
2. 定位思路總覽
| 方法 | 原理 |
|---|---|
| 1. 關(guān)鍵詞搜索 | 搜索 w=, sign=, headers, FormData, .w, .sign |
| 2. hook XMLHttpRequest / fetch | 攔截請(qǐng)求參數(shù),看 w 的生成前有哪些代碼執(zhí)行 |
| 3. 打斷點(diǎn)(XHR/fetch/send) | 手動(dòng)調(diào)試,尋找傳輸邏輯、函數(shù)調(diào)用棧 |
| 4. 控制臺(tái) hook 全局函數(shù) | 重定義 CryptoJS.MD5、btoa(),打印入?yún)⑴c結(jié)果 |
| 5. 格式化 + 搜索函數(shù)調(diào)用 | 格式化 JS 源碼,搜索可疑函數(shù)調(diào)用 |
| 6. DOM 元素關(guān)聯(lián) | 有些加密數(shù)據(jù)來源于點(diǎn)擊、坐標(biāo)、行為序列 |
| 7. AST 分析 | 靜態(tài)查找函數(shù)依賴鏈、追蹤返回值 |
| 8. Blob / Worker 調(diào)試 | 查看是否把加密邏輯放在獨(dú)立線程或動(dòng)態(tài) blob JS 里 |
3. 最常用方式詳解
【方法 1】關(guān)鍵字搜索法(適用于未嚴(yán)重混淆)
搜索:
"w="
"sign="
"form.append"
"headers"
"return {"
例子:
var t = get_w(UA, timestamp);
formData.append("w", t);
通過定位 get_w(),再深入分析。
【方法 2】hook fetch / XMLHttpRequest 攔截入?yún)?/strong>
// hook fetch
window.fetch = new Proxy(window.fetch, {
apply(target, thisArg, args) {
console.log("[fetch]", args);
return Reflect.apply(target, thisArg, args);
}
});
// hook XHR
const open = XMLHttpRequest.prototype.open;
const send = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function () {
this._url = arguments[1];
return open.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function (body) {
console.log("[XHR]", this._url, body);
return send.apply(this, arguments);
};
作用:
攔截請(qǐng)求,打印出
w,sign等參數(shù)值分析是在哪段邏輯設(shè)置的這些參數(shù)
可結(jié)合堆棧
console.trace()查看是誰生成的
【方法 3】打斷點(diǎn)(最直接有效)
位置推薦:
fetch、XMLHttpRequest.prototype.send上打斷點(diǎn)document.cookie被讀取時(shí)(可用Monitor Events)CryptoJS.MD5()、btoa()、encodeURIComponent()等加密函數(shù)調(diào)用處eval()、Function()調(diào)用處,觀察執(zhí)行前的參數(shù)
技巧:
打斷點(diǎn)后,切換到 Call Stack,順藤摸瓜,追函數(shù)棧
使用 Chrome DevTools「黑盒」方式隱藏?zé)o用框架代碼
【方法 4】hook 加密函數(shù)打印入?yún)?/strong>
常見加密函數(shù)有:
CryptoJS.MD5(xxx) CryptoJS.AES.encrypt btoa() encodeURIComponent()
可以這樣 hook:
CryptoJS.MD5 = function (arg) {
console.log("[MD5]", arg);
return originalMD5(arg); // 原函數(shù)
}
或者 hook 所有函數(shù):
Function.prototype.call = new Proxy(Function.prototype.call, {
apply(target, thisArg, args) {
console.log("[CALL]", thisArg, args);
return Reflect.apply(target, thisArg, args);
}
});
【方法 5】格式化搜索函數(shù)調(diào)用
使用 Pretty Print 格式化混淆代碼,再查找形如:
var w = a.b(c, d); // 參數(shù)生成函數(shù)
重點(diǎn)關(guān)注:
a.b這種鏈?zhǔn)秸{(diào)用,常是封裝后的加密函數(shù)把
a.b替換成打印函數(shù),輸出參數(shù)和返回值
【方法 6】AST 靜態(tài)追蹤核心函數(shù)
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const fs = require("fs");
const code = fs.readFileSync("./encrypt.js").toString();
const ast = parser.parse(code);
traverse(ast, {
CallExpression(path) {
const { callee } = path.node;
if (
callee.type === "Identifier" &&
callee.name === "get_w" // 可替換成你猜測(cè)的函數(shù)名
) {
console.log("Found w generator:", path.toString());
}
}
});
也可靜態(tài)追蹤返回的字符串是否帶有 w=...
4. 實(shí)戰(zhàn)案例簡(jiǎn)化版
例子:
function gen_w(ts, cookie, ua) {
var str = ts + "|" + cookie + "|" + ua;
return btoa(str);
}
let w = gen_w(Date.now(), document.cookie, navigator.userAgent);
分析流程:
搜索
w=,發(fā)現(xiàn)gen_w()跟進(jìn)
gen_w(),看到參數(shù)組成分析加密邏輯
btoa(str)結(jié)論:
w是 base64(ts|cookie|ua)
就可以寫腳本還原它。
5. W 參數(shù)常見特征
| 特征 | 解讀 |
|---|---|
| 固定長(zhǎng)度 | 多為 32、64、128 位(MD5、SHA1、AES 編碼) |
| 每次不同 | 含時(shí)間戳、行為 ID、cookie 等 |
| 與滑動(dòng)驗(yàn)證/行為交互相關(guān) | w 中可能包含點(diǎn)擊坐標(biāo)、移動(dòng)軌跡、session_id、lot_number 等 |
| 通常通過層層封裝 | 多層函數(shù)嵌套,?;煜P(guān)鍵函數(shù)名 |
6. 輔助工具推薦
| 工具名 | 用途 |
|---|---|
| Charles/Fiddler | 抓包查看真實(shí)參數(shù) |
| DevTools Source Map | 調(diào)試壓縮源碼前的真實(shí)結(jié)構(gòu) |
| Babel Parser + Traverse | 靜態(tài)定位函數(shù)/AST 跟蹤 |
| mitmproxy + JS hook | 手機(jī)端逆向生成參數(shù) |
| Obfuscator-IO-Deobfuscator | 一鍵還原混淆代碼 |
總結(jié)
定位加密函數(shù) = 抓到“w 參數(shù)生成”的函數(shù),并拆解其中邏輯(參數(shù)輸入、算法過程、輸出)
通常會(huì)結(jié)合這些手段:
抓包 + 調(diào)試斷點(diǎn) + 函數(shù) hook + AST 分析
同時(shí)注意
Worker/iframe/動(dòng)態(tài) eval場(chǎng)景
到此這篇關(guān)于關(guān)于JavaScript反調(diào)試與混淆識(shí)別的文章就介紹到這了,更多相關(guān)js反調(diào)試與混淆識(shí)別內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
當(dāng)達(dá)到輸入長(zhǎng)度時(shí)表單自動(dòng)切換焦點(diǎn)
給每個(gè)字段限制輸入長(zhǎng)度,當(dāng)達(dá)到輸入長(zhǎng)度時(shí)自動(dòng)切換焦點(diǎn),以增強(qiáng)表單的易用性,需要的朋友可以參考下2014-04-04
講兩件事:1.this指針的用法小探. 2.ie的attachEvent和firefox的addEventListene
講兩件事:1.this指針的用法小探. 2.ie的attachEvent和firefox的addEventListener在事件處理上的區(qū)別...2007-04-04
使用 JavaScript 在沒有插件的情況下輸入文本掩碼的示例詳解
這篇文章主要介紹了使用 JavaScript 在沒有插件的情況下輸入文本掩碼,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-06-06
javascript中加var和不加var的區(qū)別 你真的懂嗎
var 語句用于聲明變量,本文給大家介紹javascript 中加’var‘和不加'var'的區(qū)別,涉及到j(luò)avascript var相關(guān)知識(shí),對(duì)javascript var相關(guān)知識(shí)感興趣的朋友一起學(xué)習(xí)吧2016-01-01
JavaScript一文帶你玩轉(zhuǎn)web表單網(wǎng)頁
表單通常用來收集網(wǎng)頁訪問者信息,常見的表單比如搜索引擎的搜索框、各網(wǎng)頁應(yīng)用的注冊(cè)或者登陸界面等,通讀本篇對(duì)大家的學(xué)習(xí)或工作具有一定的價(jià)值,需要的朋友可以參考下2021-10-10
JavaScript使用HTML5的window.postMessage實(shí)現(xiàn)跨域通信例子
這篇文章主要介紹了JavaScript使用HTML5的window.postMessage實(shí)現(xiàn)跨域通信例子,需要的朋友可以參考下2014-04-04

