定時器
1、setTimeout
這個方法用於在指定的毫秒數之后執行某個函數,返回定時器的句柄
混合的 setTimeout()方法設置一個定時器,該定時器在定時器到期后執行一個函數或指定的一段代碼。
語法
let timeoutID = window.setTimeout(func[, delay, param1, param2, ...]); let timeoutID = scope.setTimeout(code[, delay]); let timeoutID = window.setTimeout(function, milliseconds);
說明:
timeoutID是該延時操作的數字ID, 此ID隨后可以用來作為window.clearTimeout方法的參數.func是你想要在delay毫秒之后執行的函數.code在第二種語法,是指你想要在delay毫秒之后執行的代碼字符串 (使用該語法是不推薦的, 不推薦的原因和eval()一樣)delay是延遲的毫秒數 (一秒等於1000毫秒),函數的調用會在該延遲之后發生。如果省略該參數,delay取默認值0。實際的延遲時間可能會比 delay 值長,原因請查看下面的備注。
需要注意的是,IE9 及更早的 IE 瀏覽器不支持第一種語法中向延遲函數傳遞額外參數的功能。如果你想要在IE中達到同樣的功能,你必須使用一種兼容代碼
備注: 在Gecko 13之前 (Firefox 13.0 / Thunderbird 13.0 / SeaMonkey 2.10), Gecko會給延遲函數傳遞一個額外的參數,該參數表明了此次延遲操作實際延遲的毫秒數.現在,這個非標准的參數已經不存在了.
例子
下文的例子在網頁中設置了兩個簡單的按鈕,以觸發 setTimeout 和 clearTimeout 方法:按下第一個按鈕會在 2s 后顯示一個警告對話框,並將此次 setTimeout 的延時 ID 保存起來。按下第二個按鈕可以取消這次延時調用行為。
HTML 內容
<p>Live Example</p>
<button onclick="delayedAlert();">Show an alert box after two seconds</button>
<p></p>
<button onclick="clearAlert();">Cancel alert before it happens</button>
JavaScript 內容
var timeoutID; function delayedAlert() { timeoutID = window.setTimeout(slowAlert, 2000); } function slowAlert() { alert("That was really slow!"); } function clearAlert() { window.clearTimeout(timeoutID); }
回調參數
如果你需要向你的回調函數內傳遞一個參數, 而且還需要兼容IE9及以前的版本, 由於IE不支持傳遞額外的參數 (setTimeout() 或者 setInterval()都不可以) ,但你可以引入下面的兼容代碼.該代碼能讓IE也支持符合HTML5標准的定時器函數.
/*\ |*| |*| IE-specific polyfill which enables the passage of arbitrary arguments to the |*| callback functions of javascript timers (HTML5 standard syntax). |*| |*| https://developer.mozilla.org/en-US/docs/DOM/window.setInterval |*| |*| Syntax: |*| var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]); |*| var timeoutID = window.setTimeout(code, delay); |*| var intervalID = window.setInterval(func, delay[, param1, param2, ...]); |*| var intervalID = window.setInterval(code, delay); |*| \*/
if (document.all && !window.setTimeout.isPolyfill) { var __nativeST__ = window.setTimeout; window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) { var aArgs = Array.prototype.slice.call(arguments, 2); return __nativeST__(vCallback instanceof Function ? function () { vCallback.apply(null, aArgs); } : vCallback, nDelay); }; window.setTimeout.isPolyfill = true; } if (document.all && !window.setInterval.isPolyfill) { var __nativeSI__ = window.setInterval; window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) { var aArgs = Array.prototype.slice.call(arguments, 2); return __nativeSI__(vCallback instanceof Function ? function () { vCallback.apply(null, aArgs); } : vCallback, nDelay); }; window.setInterval.isPolyfill = true; }
IE Only Fix
如果你需要單獨的針對IE9及之前瀏覽器的 hack 寫法,你可以使用 JavaScript 條件注釋:
/*@cc_on // conditional IE < 9 only fix @if (@_jscript_version <= 9) (function(f){ window.setTimeout =f(window.setTimeout); window.setInterval =f(window.setInterval); })(function(f){return function(c,t){var a=[].slice.call(arguments,2);return f(function(){c.apply(this,a)},t)}}); @end @*/
或者使用更加清晰的 IE HTML 條件注釋:
<!--[if lte IE 9]><script> (function(f){ window.setTimeout =f(window.setTimeout); window.setInterval =f(window.setInterval); })(function(f){return function(c,t){ var a=[].slice.call(arguments,2); return f(function(){c.apply(this,a)},t)} }); </script><![endif]-->
另一種方法是使用匿名函數包裹你的回調函數,這種方式要消耗更多資源:
var intervalID = setTimeout(function() { myFunc("one", "two", "three"); }, 1000);
此外,也可使用 function's bind:
setTimeout(function(arg1){}.bind(undefined, 10), 1000);
關於"this"的問題
當你向 setTimeout() (或者其他函數也行)傳遞一個函數時,該函數中的this會指向一個錯誤的值.
解釋
由setTimeout()調用的代碼運行在與所在函數完全分離的執行環境上. 這會導致,這些代碼中包含的 this 關鍵字會指向 window (或全局)對象,這和所期望的this的值是不一樣的.查看下面的例子:
myArray = ["zero", "one", "two"]; myArray.myMethod = function (sProperty) { alert(arguments.length > 0 ? this[sProperty] : this); }; myArray.myMethod(); // prints "zero,one,two"
myArray.myMethod(1); // prints "one"
setTimeout(myArray.myMethod, 1000); // prints "[object Window]" after 1 second
setTimeout(myArray.myMethod, 1500, "1"); // prints "undefined" after 1,5 seconds // let's try to pass the 'this' object
setTimeout.call(myArray, myArray.myMethod, 2000); // error: "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO: Illegal operation on WrappedNative prototype object"
setTimeout.call(myArray, myArray.myMethod, 2500, 2); // same error
正如你所看到的一樣,我們沒有任何方法將this對象傳遞給回調函數.
解決方案
一個可用的解決 "this" 問題的方法是使用兩個非原生的setTimeout() 和 setInterval() 全局函數代替原生的.該非原生的函數通過使用Function.prototype.call 方法激活了正確的作用域.下面的代碼顯示了應該如何替換:
// Enable the passage of the 'this' object through the JavaScript timers
var __nativeST__ = window.setTimeout, __nativeSI__ = window.setInterval; window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) { var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2); return __nativeST__(vCallback instanceof Function ? function () { vCallback.apply(oThis, aArgs); } : vCallback, nDelay); }; window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) { var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2); return __nativeSI__(vCallback instanceof Function ? function () { vCallback.apply(oThis, aArgs); } : vCallback, nDelay); };
備注: 這兩個替換也讓 IE支持了符合 HTML5 標准的定時器函數。所以也能作為一個 polyfills
新特性檢測:
myArray = ["zero", "one", "two"]; myArray.myMethod = function (sProperty) { alert(arguments.length > 0 ? this[sProperty] : this); }; setTimeout(alert, 1500, "Hello world!"); // the standard use of setTimeout and setInterval is preserved, but...
setTimeout.call(myArray, myArray.myMethod, 2000); // prints "zero,one,two" after 2 seconds
setTimeout.call(myArray, myArray.myMethod, 2500, 2); // prints "two" after 2,5 seconds
針對這個問題並沒有原生的解決方案。
注:JavaScript 1.8.5 引入了 Function.prototype.bind() 方法,該方法允許顯式地指定函數調用時 this 所指向的值 。該方法可以幫助你解決 this 指向不確定的問題。
使用bind的例子:
myArray = ["zero", "one", "two"]; myBoundMethod = (function (sProperty) { console.log(arguments.length > 0 ? this[sProperty] : this); }).bind(myArray); myBoundMethod(); // prints "zero,one,two" because 'this' is bound to myArray in the function
myBoundMethod(1); // prints "one"
setTimeout(myBoundMethod, 1000); // still prints "zero,one,two" after 1 second because of the binding
setTimeout(myBoundMethod, 1500, "1"); // prints "one" after 1.5 seconds
備注
你可以使用 window.clearTimeout()來取消延遲操作。如果你希望你的代碼被重復的調用 (比如每 N 毫秒一次),考慮使用 window.setInterval()。
記住這一點:只有當調用setTimeout()的線程停止后,函數或代碼段才能繼續執行。
傳遞字符串字面量
向setTimeout()傳遞一個字符串而不是函數會遭受到與使用eval一樣的風險.
// 推薦
window.setTimeout(function() { alert("Hello World!"); }, 500); // 不推薦
window.setTimeout("alert(\"Hello World!\");", 500);
字符串會在全局作用域內被解釋執行,所以當setTimeout()函數執行完畢后,字符串中的變量不可用.
2、 setInterval
這個方法用於循環地執行某個函數,返回定時器的句柄
語法
var intervalID = window.setInterval(func, delay[, param1, param2, ...]); var intervalID = window.setInterval(code, delay);
參數
intervalID是此重復操作的唯一辨識符,可以作為參數傳給clearInterval()。func是你想要重復調用的函數。code是另一種語法的應用,是指你想要重復執行的一段字符串構成的代碼(使用該語法是不推薦的,不推薦的原因和eval()一樣)。delay是每次延遲的毫秒數 (一秒等於1000毫秒),函數的每次調用會在該延遲之后發生。和setTimeout一樣,實際的延遲時間可能會稍長一點。
需要注意的是,IE不支持第一種語法中向延遲函數傳遞額外參數的功能.如果你想要在IE中達到同樣的功能,你必須使用一種兼容代碼
備注: 在Gecko 13之前 (Firefox 13.0 / Thunderbird 13.0 / SeaMonkey 2.10), Gecko會給延遲函數傳遞一個額外的參數,該參數表明了此次延遲操作實際延遲的毫秒數。現在,這個非標准的參數已經不存在了。
重復調用一個函數或執行一個代碼段,以固定的時間延遲在每次調用之間。返回一個 intervalID。
例1:基本用法
var intervalID = window.setInterval(animate, 500);
例2:兩種顏色的切換
下面的例子里會每隔一秒就調用函數flashtext()一次,直至你通過按下Stop按鈕來清除本次重復操作的唯一辨識符intervalID。
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
<title>setInterval/clearInterval example</title>
<script type="text/javascript">
var nIntervId; function changeColor() { nIntervId = setInterval(flashText, 500); } function flashText() { var oElem = document.getElementById("my_box"); oElem.style.color = oElem.style.color == "red" ? "blue" : "red"; } function stopTextColor() { clearInterval(nIntervId); } </script>
</head>
<body onload="changeColor();">
<div id="my_box">
<p>Hello World</p>
</div>
<button onclick="stopTextColor();">Stop</button>
</body>
</html>
例3:打字機的效果
下面這個例子通過鍵入、刪除和再次鍵入所有NodeList中的符合的特定selector的字符,以達到打字機的效果。
<!doctype html> <html> <head> <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" /> <title>JavaScript Typewriter - MDN Example</title> <script type="text/javascript"> function Typewriter (sSelector, nRate) { function clean () { clearInterval(nIntervId); bTyping = false; bStart = true; oCurrent = null; aSheets.length = nIdx = 0; } function scroll (oSheet, nPos, bEraseAndStop) { if (!oSheet.hasOwnProperty("parts") || aMap.length < nPos) { return true; } var oRel, bExit = false; if (aMap.length === nPos) { aMap.push(0); } while (aMap[nPos] < oSheet.parts.length) { oRel = oSheet.parts[aMap[nPos]]; scroll(oRel, nPos + 1, bEraseAndStop) ? aMap[nPos]++ : bExit = true; if (bEraseAndStop && (oRel.ref.nodeType - 1 | 1) === 3 && oRel.ref.nodeValue) { bExit = true; oCurrent = oRel.ref; sPart = oCurrent.nodeValue; oCurrent.nodeValue = ""; } oSheet.ref.appendChild(oRel.ref); if (bExit) { return false; } } aMap.length--; return true; } function typewrite () { if (sPart.length === 0 && scroll(aSheets[nIdx], 0, true) && nIdx++ === aSheets.length - 1) { clean(); return; } oCurrent.nodeValue += sPart.charAt(0); sPart = sPart.slice(1); } function Sheet (oNode) { this.ref = oNode; if (!oNode.hasChildNodes()) { return; } this.parts = Array.prototype.slice.call(oNode.childNodes); for (var nChild = 0; nChild < this.parts.length; nChild++) { oNode.removeChild(this.parts[nChild]); this.parts[nChild] = new Sheet(this.parts[nChild]); } } var nIntervId, oCurrent = null, bTyping = false, bStart = true, nIdx = 0, sPart = "", aSheets = [], aMap = []; this.rate = nRate || 100; this.play = function () { if (bTyping) { return; } if (bStart) { var aItems = document.querySelectorAll(sSelector); if (aItems.length === 0) { return; } for (var nItem = 0; nItem < aItems.length; nItem++) { aSheets.push(new Sheet(aItems[nItem])); /* Uncomment the following line if you have previously hidden your elements via CSS: */ // aItems[nItem].style.visibility = "visible"; } bStart = false; } nIntervId = setInterval(typewrite, this.rate); bTyping = true; }; this.pause = function () { clearInterval(nIntervId); bTyping = false; }; this.terminate = function () { oCurrent.nodeValue += sPart; sPart = ""; for (nIdx; nIdx < aSheets.length; scroll(aSheets[nIdx++], 0, false)); clean(); }; } /* usage: */ var oTWExample1 = new Typewriter(/* elements: */ "#article, h1, #info, #copyleft", /* frame rate (optional): */ 15); /* default frame rate is 100: */ var oTWExample2 = new Typewriter("#controls"); /* you can also change the frame rate value modifying the "rate" property; for example: */ // oTWExample2.rate = 150; onload = function () { oTWExample1.play(); oTWExample2.play(); }; </script> <style type="text/css"> span.intLink, a, a:visited { cursor: pointer; color: #000000; text-decoration: underline; } #info { width: 180px; height: 150px; float: right; padding: 4px; overflow: auto; font-size: 12px; margin: 4px; border-radius: 5px; /* visibility: hidden; */ } </style> </head> <body> <p id="copyleft" style="font-style: italic; font-size: 12px; text-align: center;">CopyLeft 2012 by <a href="https://developer.mozilla.org/" target="_blank">Mozilla Developer Network</a></p> <p id="controls" style="text-align: center;">[ <span class="intLink" onclick="oTWExample1.play();">Play</span> | <span class="intLink" onclick="oTWExample1.pause();">Pause</span>
| <span class="intLink" onclick="oTWExample1.terminate();">Terminate</span> ]</p> <div id="info"> Vivamus blandit massa ut metus mattis in fringilla lectus imperdiet. Proin ac ante a felis ornare vehicula. Fusce pellentesque lacus vitae eros convallis ut mollis magna pellentesque.
Pellentesque placerat enim at lacus ultricies vitae facilisis nisi fringilla. In tincidunt tincidunt tincidunt. </div> <h1>JavaScript Typewriter</h1> <div id="article"> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ultrices dolor ac dolor imperdiet ullamcorper. Suspendisse quam libero, luctus auctor mollis sed, malesuada condimentum magna.
Quisque in ante tellus, in placerat est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec a mi magna, quis mattis dolor. Etiam sit amet ligula
quis urna auctor imperdiet nec faucibus ante. Mauris vel consectetur dolor. Nunc eget elit eget velit pulvinar fringilla consectetur aliquam purus. Curabitur convallis, justo posuere porta egestas,
velit erat ornare tortor, non viverra justo diam eget arcu. Phasellus adipiscing fermentum nibh ac commodo. Nam turpis nunc, suscipit a hendrerit vitae, volutpat non ipsum.</p> <form name="myForm"> <p>Phasellus ac nisl lorem: <input type="text" name="email" /><br /> <textarea name="comment" style="width: 400px; height: 200px;">
Nullam commodo suscipit lacus non aliquet. Phasellus ac nisl lorem, sed facilisis ligula. Nam cursus lobortis placerat. Sed dui nisi, elementum eu sodales ac, placerat sit amet mauris. Pellentesque
dapibus tellus ut ipsum aliquam eu auctor dui vehicula. Quisque ultrices laoreet erat, at ultrices tortor sodales non. Sed venenatis luctus magna, ultricies ultricies nunc fringilla eget. Praesent scelerisque
urna vitae nibh tristique varius consequat neque luctus. Integer ornare, erat a porta tempus, velit justo fermentum elit, a fermentum metus nisi eu ipsum. Vivamus eget augue vel dui viverra adipiscing congue ut massa.
Praesent vitae eros erat, pulvinar laoreet magna. Maecenas vestibulum mollis nunc in posuere. Pellentesque sit amet metus a turpis lobortis tempor eu vel tortor. Cras sodales eleifend interdum.</textarea></p> <p><input type="submit" value="Send" /> </form> <p>Duis lobortis sapien quis nisl luctus porttitor. In tempor semper libero, eu tincidunt dolor eleifend sit amet. Ut nec velit in dolor tincidunt rhoncus non non diam. Morbi auctor ornare orci, non euismod felis
gravida nec. Curabitur elementum nisi a eros rutrum nec blandit diam placerat. Aenean tincidunt risus ut nisi consectetur cursus. Ut vitae quam elit. Donec dignissim est in quam tempor consequat. Aliquam aliquam
diam non felis convallis suscipit. Nulla facilisi. Donec lacus risus, dignissim et fringilla et, egestas vel eros. Duis malesuada accumsan dui, at fringilla mauris bibStartum quis. Cras adipiscing ultricies fermentum.
Praesent bibStartum condimentum feugiat.</p> <p>Nam faucibus, ligula eu fringilla pulvinar, lectus tellus iaculis nunc, vitae scelerisque metus leo non metus. Proin mattis lobortis lobortis. Quisque accumsan faucibus erat, vel varius tortor ultricies ac.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nec libero nunc. Nullam tortor nunc, elementum a consectetur et, ultrices eu orci. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Pellentesque a nisl eu sem vehicula egestas.</p> </div> </body> </html>
倒計時:
<div class="box">
<span class="h time">12</span>
<span>:</span>
<span class="m time">12</span>
<span>:</span>
<span class="s time">55</span>
</div>
<script>
var div = document.body.children[0]; //所有的span
var spans = div.children; var second = spans[4]; var minutes = spans[2]; console.log(minutes); //每隔1秒鍾修改秒數
setInterval(function(){ // 先獲取舊的數字,自增,重新賦值
var old = parseInt(second.innerText); var m = parseInt(minutes.innerText); old +=1; //以上是秒的邏輯,到分的邏輯
//當秒數大於59,就滿一分鍾,分自增,秒重置
if(old >59){ old = 0; //獲取分的值,自增,重新賦值
m += 1; } //小時的邏輯也是當分超過59,讓小時自增1,分重置
second.innerText = old; minutes.innerText = m; },1000); </script>
簡單動畫效果的實現
<!DOCTYPE html>
<html>
<head lang="zh-CN">
<meta charset="UTF-8">
<title></title>
<style> * { margin: 0; padding: 0;
} #box { width: 100px; height: 100px; background-color: #0a0;
/*margin-left: 500px;*/
/*一般做動畫,都會使用定位,脫標定位 -- 絕對,固定*/ position: absolute; top: 200px; left: 0px;
} .boxw { width: 100px; height: 100px; background: #cccccc; float: left;
}
</style>
</head>
<body>
<input type="button" value="開始運動" id="btn"/>
<div id="box"></div>
<div class="boxw"></div>
<script>
//動畫原理:每隔一定的時間,修改能控制元素位置的屬性,就能達到動起來的效果,當位置到達800的時候停下來
var curerntLeft = 0; var box = document.getElementById("box"); document.getElementById("btn").onclick = function(){ var timerId = setInterval(function(){ //每次獲取當前位置,進行自增,最后重新賦值
var step = 10;//每次移動的步長
curerntLeft += step; box.style.left = curerntLeft + "px"; //判斷當前位置是否大於等於800,如果是,就停下來
if(curerntLeft >= 800){ clearInterval(timerId); } console.log(123456); },20);//人的肉眼一般可識別的間隔為:0.03秒 = 30毫秒
} </script>
</body>
</html>
回調參數
如果你想通過你的函數傳遞回一個參數,而且還要兼容IE,由於IE不支持傳遞額外的參數 (setTimeout() 或者 setInterval()都不可以) ,你可以引入下面的兼容代碼。該代碼能讓IE也支持符合HTML5標准的定時器函數。
/*\ |*| |*| IE-specific polyfill which enables the passage of arbitrary arguments to the |*| callback functions of javascript timers (HTML5 standard syntax). |*| |*| https://developer.mozilla.org/en-US/docs/DOM/window.setInterval |*| |*| Syntax: |*| var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]); |*| var timeoutID = window.setTimeout(code, delay); |*| var intervalID = window.setInterval(func, delay[, param1, param2, ...]); |*| var intervalID = window.setInterval(code, delay); |*| \*/
if (document.all && !window.setTimeout.isPolyfill) { var __nativeST__ = window.setTimeout; window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) { var aArgs = Array.prototype.slice.call(arguments, 2); return __nativeST__(vCallback instanceof Function ? function () { vCallback.apply(null, aArgs); } : vCallback, nDelay); }; window.setTimeout.isPolyfill = true; } if (document.all && !window.setInterval.isPolyfill) { var __nativeSI__ = window.setInterval; window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) { var aArgs = Array.prototype.slice.call(arguments, 2); return __nativeSI__(vCallback instanceof Function ? function () { vCallback.apply(null, aArgs); } : vCallback, nDelay); }; window.setInterval.isPolyfill = true; }
