關(guān)于JavaScript反調(diào)試與混淆識(shí)別舉例詳解
一、常見反調(diào)試手段識(shí)別
1. debugger 死循環(huán)(阻塞調(diào)試器)
樣例代碼:
while (true) { debugger; }
原理:
每次執(zhí)行到
debugger
語(yǔ)句,如果 DevTools 打開,將自動(dòng)觸發(fā)斷點(diǎn)。如果在死循環(huán)中,調(diào)試器會(huì)被頻繁打斷,卡死頁(yè)面或無(wú)法操作。
特征識(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í),如果超過(guò)閾值,說(shuō)明你在調(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í)行語(yǔ)句。動(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ù)組通過(guò)索引訪問(wèn),變量命名毫無(wú)意義(如
_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ī)索引訪問(wèn) | 還原數(shù)組映射 / 替換所有訪問(wèn)語(yǔ)句 |
三、AST 抽象語(yǔ)法樹分析
AST(抽象語(yǔ)法樹) 是程序源代碼的結(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é)果 |
刪除垃圾代碼(無(wú)用判斷等) | 刪除某些結(jié)構(gòu)的語(yǔ)句(如 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
(屬性訪問(wèn))如果是
_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. 輔助工具推薦
工具 | 說(shuō)明 |
---|---|
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ù)通過(guò) JS 中隱藏/混淆的函數(shù)生成,是反爬的關(guān)鍵一環(huán)
我們的任務(wù)是定位并還原這些函數(shù)!
1. 典型例子
例如某請(qǐng)求:
POST /api/check headers: w: "e72fa5b18d320...(加密值)"
需要搞清楚:
誰(shuí)生成了
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ù)來(lái)源于點(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);
通過(guò)定位 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()
查看是誰(shuí)生成的
【方法 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 等 |
通常通過(guò)層層封裝 | 多層函數(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ù)輸入、算法過(guò)程、輸出)
通常會(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 在沒(méi)有插件的情況下輸入文本掩碼的示例詳解
這篇文章主要介紹了使用 JavaScript 在沒(méi)有插件的情況下輸入文本掩碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-06-06javascript中加var和不加var的區(qū)別 你真的懂嗎
var 語(yǔ)句用于聲明變量,本文給大家介紹javascript 中加’var‘和不加'var'的區(qū)別,涉及到j(luò)avascript var相關(guān)知識(shí),對(duì)javascript var相關(guān)知識(shí)感興趣的朋友一起學(xué)習(xí)吧2016-01-01JavaScript一文帶你玩轉(zhuǎn)web表單網(wǎng)頁(yè)
表單通常用來(lái)收集網(wǎng)頁(yè)訪問(wèn)者信息,常見的表單比如搜索引擎的搜索框、各網(wǎng)頁(yè)應(yīng)用的注冊(cè)或者登陸界面等,通讀本篇對(duì)大家的學(xué)習(xí)或工作具有一定的價(jià)值,需要的朋友可以參考下2021-10-10JavaScript使用HTML5的window.postMessage實(shí)現(xiàn)跨域通信例子
這篇文章主要介紹了JavaScript使用HTML5的window.postMessage實(shí)現(xiàn)跨域通信例子,需要的朋友可以參考下2014-04-04