來源: ApacheCN『JavaScript 編程精解 中文第三版』翻譯項目原文:Handling Eventsjavascript
譯者:飛龍html
協議:CC BY-NC-SA 4.0java
自豪地採用谷歌翻譯node
部分參考了《JavaScript 編程精解(第 2 版)》git
你對你的大腦擁有控制權,而不是外部事件。認識到這一點,你就找到了力量。程序員
馬可·奧勒留,《沉思錄》github
有些程序處理用戶的直接輸入,好比鼠標和鍵盤動做。這種輸入方式不是組織整齊的數據結構 - 它是一次一個地,實時地出現的,而且指望程序在發生時做出響應。apache
想象一下,有一個接口,若想知道鍵盤上是否有一個鍵是否被按下,惟一的方法是讀取那個按鍵的當前狀態。爲了可以響應按鍵動做,你須要不斷讀取鍵盤狀態,以在按鍵被釋放以前捕捉到按下狀態。這種方法在執行時間密集計算時很是危險,由於你可能錯過按鍵事件。編程
一些原始機器能夠像那樣處理輸入。有一種更進一步的方法,硬件或操做系統發現按鍵時間並將其放入隊列中。程序能夠週期性地檢查隊列,等待新事件並在發現事件時進行響應。數組
固然,程序必須記得監視隊列,並常常作這種事,由於任什麼時候候,按鍵被按下和程序發現事件之間都會使得軟件反應遲鈍。該方法被稱爲輪詢。大多數程序員更但願避免這種方法。
一個更好的機制是,系統在發生事件時主動通知咱們的代碼。瀏覽器實現了這種特性,支持咱們將函數註冊爲特定事件的處理器。
<p>Click this document to activate the handler.</p> <script> window.addEventListener("click", () => { console.log("You knocked?"); }); </script>
window
綁定指向瀏覽器提供的內置對象。 它表明包含文檔的瀏覽器窗口。 調用它的addEventListener
方法註冊第二個參數,以便在第一個參數描述的事件發生時調用它。
每一個瀏覽器事件處理器被註冊在上下文中。在爲整個窗口註冊處理器以前,咱們在window
對象上調用了addEventListener
。 這種方法也能夠在 DOM 元素和一些其餘類型的對象上找到。 僅當事件發生在其註冊對象的上下文中時,才調用事件監聽器。
<button>Click me</button> <p>No handler here.</p> <script> let button = document.querySelector("button"); button.addEventListener("click", () => { console.log("Button clicked."); }); </script>
示例代碼中將處理器附加到按鈕節點上。所以,點擊按鈕時會觸發並執行處理器,而點擊文檔的其餘部分則沒有反應。
向節點提供onclick
屬性也有相似效果。這適用於大多數類型的事件 - 您能夠爲屬性附加處理器,屬性名稱爲前面帶有on
的事件名稱。
可是一個節點只能有一個onclick
屬性,因此你只能用這種方式爲每一個節點註冊一個處理器。 addEventListener
方法容許您添加任意數量的處理器,所以即便元素上已經存在另外一個處理器,添加處理器也是安全的。
removeEventListener
方法將刪除一個處理器,使用相似於addEventListener
的參數調用。
<button>Act-once button</button> <script> let button = document.querySelector("button"); function once() { console.log("Done."); button.removeEventListener("click", once); } button.addEventListener("click", once); </script>
賦予removeEventListener
的函數必須是賦予addEventListener
的徹底相同的函數值。 所以,要註銷一個處理其,您須要爲該函數提供一個名稱(在本例中爲once
),以便可以將相同的函數值傳遞給這兩個方法。
雖然目前爲止咱們忽略了它,事件處理器函數做爲對象傳遞:事件(Event)對象。這個對象持有事件的額外信息。例如,若是咱們想知道哪一個鼠標按鍵被按下,咱們能夠查看事件對象的which屬性。
<button>Click me any way you want</button> <script> let button = document.querySelector("button"); button.addEventListener("mousedown", event => { if (event.button == 0) { console.log("Left button"); } else if (event.button == 1) { console.log("Middle button"); } else if (event.button == 2) { console.log("Right button"); } }); </script>
存儲在各類類型事件對象中的信息是有差異的。隨後本章將會討論許多類型的事件。對象的type
屬性通常持有一個字符串,表示事件(例如"click"
和"mousedown"
)。
對於大多數事件類型,在具備子節點的節點上註冊的處理器,也將接收發生在子節點中的事件。若點擊一個段落中的按鈕,段落的事件處理器也會收到點擊事件。
但若段落和按鈕都有事件處理器,則先執行最特殊的事件處理器(按鈕的事件處理器)。也就是說事件向外傳播,從觸發事件的節點到其父節點,最後直到文檔根節點。最後,當某個特定節點上註冊的全部事件處理器按其順序所有執行完畢後,窗口對象的事件處理器纔有機會響應事件。
事件處理器任什麼時候候均可以調用事件對象的stopPropagation
方法,阻止事件進一步傳播。該方法有時很實用,例如,你將一個按鈕放在另外一個可點擊元素中,但你不但願點擊該按鈕會激活外部元素的點擊行爲。
下面的示例代碼將mousedown
處理器註冊到按鈕和其外部的段落節點上。在按鈕上點擊鼠標右鍵,按鈕的處理器會調用stopPropagation
,調度段落上的事件處理器執行。當點擊鼠標其餘鍵時,兩個處理器都會執行。
<p>A paragraph with a <button>button</button>.</p> <script> let para = document.querySelector("p"); let button = document.querySelector("button"); para.addEventListener("mousedown", () => { console.log("Handler for paragraph."); }); button.addEventListener("mousedown", event => { console.log("Handler for button."); if (event.button == 2) event.stopPropagation(); }); </script>
大多數事件對象都有target
屬性,指的是事件來源節點。你能夠根據該屬性防止無心中處理了傳播自其餘節點的事件。
咱們也可使用target
屬性來建立出特定類型事件的處理網絡。例如,若是一個節點中包含了很長的按鈕列表,比較方便的處理方式是在外部節點上註冊一個點擊事件處理器,並根據事件的target
屬性來區分用戶按下了哪一個按鈕,而不是爲每一個按鈕都註冊獨立的事件處理器。
<button>A</button> <button>B</button> <button>C</button> <script> document.body.addEventListener("click", event => { if (event.target.nodeName == "BUTTON") { console.log("Clicked", event.target.textContent); } }); </script>
大多數事件都有與其關聯的默認動做。若點擊連接,就會跳轉到連接目標。若點擊向下的箭頭,瀏覽器會向下翻頁。若右擊鼠標,能夠獲得一個上下文菜單等。
對於大多數類型的事件,JavaScript 事件處理器會在默認行爲發生以前調用。若事件處理器不但願執行默認行爲(一般是由於已經處理了該事件),會調用preventDefault
事件對象的方法。
你能夠實現你本身的鍵盤快捷鍵或交互式菜單。你也能夠干擾用戶指望的行爲。例如,這裏實現一個沒法跳轉的連接。
<a href="https://developer.mozilla.org/">MDN</a> <script> let link = document.querySelector("a"); link.addEventListener("click", event => { console.log("Nope."); event.preventDefault(); }); </script>
除非你有很是充足的理由,不然不要這樣作。當預期的行爲被打破時,使用你的頁面的人會感到不快。
在有些瀏覽器中,你徹底沒法攔截某些事件。好比在 Chrome 中,關閉鍵盤快捷鍵(CTRL-W
或COMMAND-W
)沒法由 JavaScript 處理。
當按下鍵盤上的按鍵時,瀏覽器會觸發"keydown"
事件。當鬆開按鍵時,會觸發"keyup"
事件。
<p>This page turns violet when you hold the V key.</p> <script> window.addEventListener("keydown", event => { if (event.key == "v") { document.body.style.background = "violet"; } }); window.addEventListener("keyup", event => { if (event.key == "v") { document.body.style.background = ""; } }); </script>
儘管從keydown
這個事件名上看應該是物理按鍵按下時觸發,但當持續按下某個按鍵時,會循環觸發該事件。有時,你想謹慎對待它。例如,若是您在按下某個按鍵時向 DOM 添加按鈕,而且在釋放按鍵時再次將其刪除,則可能會在按住某個按鍵的時間過長時,意外添加數百個按鈕。
該示例查看了事件對象的key
屬性,來查看事件關於哪一個鍵。 該屬性包含一個字符串,對於大多數鍵,它對應於按下該鍵時將鍵入的內容。 對於像Enter
這樣的特殊鍵,它包含一個用於命名鍵的字符串(在本例中爲"Enter"
)。 若是你按住一個鍵的同時按住Shift
鍵,這也可能影響鍵的名稱 - "v"
變爲"V"
,"1"
可能變成"!"
,這是按下Shift-1
鍵 在鍵盤上產生的東西。
諸如shift
、ctrl
、alt
和meta
(Mac 上的command
)之類的修飾按鍵會像普通按鍵同樣產生事件。但在查找組合鍵時,你也能夠查看鍵盤和鼠標事件的shiftKey
、ctrlKey
、altKey
和metaKey
屬性來判斷這些鍵是否被按下。
<p>Press Ctrl-Space to continue.</p> <script> window.addEventListener("keydown", event => { if (event.key == " " && event.ctrlKey) { console.log("Continuing!"); } }); </script>
按鍵事件發生的 DOM 節點取決於按下按鍵時具備焦點的元素。 大多數節點不能擁有焦點,除非你給他們一個tabindex
屬性,但像連接,按鈕和表單字段能夠。 咱們將在第 18 章中回顧表單字段。 當沒有特別的焦點時,document.body
充當按鍵事件的目標節點。
當用戶鍵入文本時,使用按鍵事件來肯定正在鍵入的內容是有問題的。 某些平臺,尤爲是 Android 手機上的虛擬鍵盤,不會觸發按鍵事件。 但即便你有一個老式鍵盤,某些類型的文本輸入也不能直接匹配按鍵,例如其腳本不適合鍵盤的人所使用的 IME(「輸入法編輯器」)軟件 ,其中組合多個熱鍵來建立字符。
要注意何時輸入了內容,每當用戶更改其內容時,能夠鍵入的元素(例如<input>
和<textarea>
標籤)觸發"input"
事件。爲了得到輸入的實際內容,最好直接從焦點字段中讀取它。 第 18 章將展現如何實現。
目前有兩種普遍使用的方式,用於指向屏幕上的東西:鼠標(包括相似鼠標的設備,如觸摸板和軌跡球)和觸摸屏。 它們產生不一樣類型的事件。
點擊鼠標按鍵會觸發一系列事件。"mousedown"
事件和"mouseup"
事件相似於"keydown"
和"keyup"
事件,當鼠標按鈕按下或釋放時觸發。當事件發生時,由鼠標指針下方的 DOM 節點觸發事件。
在mouseup
事件後,包含鼠標按下與釋放的特定節點會觸發"click"
事件。例如,若是我在一個段落上按下鼠標,移動到另外一個段落上釋放鼠標,"click"
事件會發生在包含這兩個段落的元素上。
若兩次點擊事件觸發時機接近,則在第二次點擊事件以後,也會觸發"dbclick"
(雙擊,double-click)事件。
爲了得到鼠標事件觸發的精確信息,你能夠查看事件中的clientX
和clientY
屬性,包含了事件相對於窗口左上角的座標(以像素爲單位)。或pageX
和pageY
,它們相對於整個文檔的左上角(當窗口被滾動時可能不一樣)。
下面的代碼實現了簡單的繪圖程序。每次點擊文檔時,會在鼠標指針下添加一個點。還有一個稍微優化的繪圖程序,請參見第 19 章。
<style> body { height: 200px; background: beige; } .dot { height: 8px; width: 8px; border-radius: 4px; /* rounds corners */ background: blue; position: absolute; } </style> <script> window.addEventListener("click", event => { let dot = document.createElement("div"); dot.className = "dot"; dot.style.left = (event.pageX - 4) + "px"; dot.style.top = (event.pageY - 4) + "px"; document.body.appendChild(dot); }); </script>
每次鼠標移動時都會觸發"mousemove"
事件。該事件可用於跟蹤鼠標位置。當實現某些形式的鼠標拖拽功能時,該事件很是有用。
舉一個例子,下面的程序展現一條欄,並設置一個事件處理器,當向左拖動這個欄時,會使其變窄,若向右拖動則變寬。
<p>Drag the bar to change its width:</p> <div style="background: orange; width: 60px; height: 20px"> </div> <script> let lastX; // Tracks the last observed mouse X position let bar = document.querySelector("div"); bar.addEventListener("mousedown", event => { if (event.button == 0) { lastX = event.clientX; window.addEventListener("mousemove", moved); event.preventDefault(); // Prevent selection } }); function moved(event) { if (event.buttons == 0) { window.removeEventListener("mousemove", moved); } else { let dist = event.clientX - lastX; let newWidth = Math.max(10, bar.offsetWidth + dist); bar.style.width = newWidth + "px"; lastX = event.clientX; } } </script>
請注意,mousemove
處理器註冊在窗口對象上。即便鼠標在改變窗口尺寸時在欄外側移動,只要按住按鈕,咱們仍然想要更新其大小。
釋放鼠標按鍵時,咱們必須中止調整欄的大小。 爲此,咱們可使用buttons
屬性(注意複數形式),它告訴咱們當前按下的按鍵。 當它爲零時,沒有按下按鍵。 當按鍵被按住時,其值是這些按鍵的代碼總和 - 左鍵代碼爲 1,右鍵爲 2,中鍵爲 4。 這樣,您能夠經過獲取buttons
的剩餘值及其代碼,來檢查是否按下了給定按鍵。
請注意,這些代碼的順序與button
使用的順序不一樣,中鍵位於右鍵以前。 如前所述,一致性並非瀏覽器編程接口的強項。
咱們使用的圖形瀏覽器的風格,是考慮到鼠標界面的狀況下而設計的,那個時候觸摸屏很是罕見。 爲了使網絡在早期的觸摸屏手機上「工做」,在某種程度上,這些設備的瀏覽器僞裝觸摸事件是鼠標事件。 若是你點擊你的屏幕,你會獲得'mousedown'
,'mouseup'
和'click'
事件。
可是這種錯覺不是很健壯。 觸摸屏與鼠標的工做方式不一樣:它沒有多個按鈕,當手指不在屏幕上時不能跟蹤手指(來模擬"mousemove"
),而且容許多個手指同時在屏幕上。
鼠標事件只涵蓋了簡單狀況下的觸摸交互 - 若是您爲按鈕添加"click"
處理器,觸摸用戶仍然可使用它。 可是像上一個示例中的可調整大小的欄在觸摸屏上不起做用。
觸摸交互觸發了特定的事件類型。 當手指開始觸摸屏幕時,您會看到'touchstart'
事件。 當它在觸摸中移動時,觸發"touchmove"
事件。 最後,當它中止觸摸屏幕時,您會看到"touchend"
事件。
因爲許多觸摸屏能夠同時檢測多個手指,這些事件沒有與其關聯的一組座標。 相反,它們的事件對象擁有touches
屬性,它擁有一個類數組對象,每一個對象都有本身的clientX
,clientY
,pageX
和pageY
屬性。
你能夠這樣,在每一個觸摸手指周圍顯示紅色圓圈。
<style> dot { position: absolute; display: block; border: 2px solid red; border-radius: 50px; height: 100px; width: 100px; } </style> <p>Touch this page</p> <script> function update(event) { for (let dot; dot = document.querySelector("dot");) { dot.remove(); } for (let i = 0; i < event.touches.length; i++) { let {pageX, pageY} = event.touches[i]; let dot = document.createElement("dot"); dot.style.left = (pageX - 50) + "px"; dot.style.top = (pageY - 50) + "px"; document.body.appendChild(dot); } } window.addEventListener("touchstart", update); window.addEventListener("touchmove", update); window.addEventListener("touchend", update); </script>
您常常但願在觸摸事件處理器中調用preventDefault
,來覆蓋瀏覽器的默認行爲(可能包括在滑動時滾動頁面),並防止觸發鼠標事件,您也可能擁有它的處理器。
每當元素滾動時,會觸發scroll
事件。該事件用處極多,好比知道用戶當前查看的元素(禁用用戶視線之外的動畫,或向邪惡的指揮部發送監視報告),或展現一些滾動的跡象(經過高亮表格的部份內容,或顯示頁碼)。
如下示例在文檔上方繪製一個進度條,並在您向下滾動時更新它來填充:
<style> #progress { border-bottom: 2px solid blue; width: 0; position: fixed; top: 0; left: 0; } </style> <div id="progress"></div> <script> // Create some content document.body.appendChild(document.createTextNode( "supercalifragilisticexpialidocious ".repeat(1000))); let bar = document.querySelector("#progress"); window.addEventListener("scroll", () => { let max = document.body.scrollHeight - innerHeight; bar.style.width = `${(pageYOffset / max) * 100}%`; }); </script>
將元素的position
屬性指定爲fixed
時,其行爲和absolute
很像,但能夠防止在文檔滾動時期跟着文檔一塊兒滾動。其效果是讓咱們的進度條呆在最頂上。 改變其寬度來指示當前進度。 在設置寬度時,咱們使用%
而不是px
做爲單位,使元素的大小相對於頁面寬度。
innerHeight
全局綁定是窗口高度,咱們必需要減去滾動條的高度。你點擊文檔底部的時候是沒法繼續滾動的。對於窗口高度來講,也存在innerWidth
。使用pageYOffset
(當前滾動位置)除以最大滾動位置,並乘以 100,就能夠獲得進度條長度。
調用滾動事件的preventDefault
沒法阻止滾動。實際上,事件處理器是在進行滾動以後才觸發的。
當元素得到焦點時,瀏覽器會觸發其上的focus
事件。當失去焦點時,元素會得到blur
事件。
與前文討論的事件不一樣,這兩個事件不會傳播。子元素得到或失去焦點時,不會激活父元素的處理器。
下面的示例中,文本域在擁有焦點時會顯示幫助文本。
<p>Name: <input type="text" data-help="Your full name"></p> <p>Age: <input type="text" data-help="Your age in years"></p> <p id="help"></p> <script> let help = document.querySelector("#help"); let fields = document.querySelectorAll("input"); for (let field of Array.from(fields)) { field.addEventListener("focus", event => { let text = event.target.getAttribute("data-help"); help.textContent = text; }); field.addEventListener("blur", event => { help.textContent = ""; }); } </script>
當用戶從瀏覽器標籤或窗口移開時,窗口對象會收到focus
事件,當移動到標籤或窗口上時,則收到blur
事件。
當界面結束裝載時,會觸發窗口對象和文檔body
對象的"load"
事件。該事件一般用於在當整個文檔構建完成時,進行初始化。請記住<script>標
籤的內容是一遇到就執行的。這可能太早了,好比有時腳本須要處理在<script>
標籤後出現的內容。
諸如image
或script
這類會裝載外部文件的標籤都有load
事件,指示其引用文件裝載完畢。相似於焦點事件,裝載事件是不會傳播的。
當頁面關閉或跳轉(好比跳轉到一個連接)時,會觸發beforeunload
事件。該事件用於防止用戶忽然關閉文檔而丟失工做結果。你沒法使用preventDefault
方法阻止頁面卸載。它經過從處理器返回非空值來完成。當你這樣作時,瀏覽器會經過顯示一個對話框,詢問用戶是否關閉頁面的對話框中。該機制確保用戶能夠離開,即便在那些想要留住用戶,強制用戶看廣告的惡意頁面上,也是這樣。
在事件循環的上下文中,如第 11 章中所述,瀏覽器事件處理器的行爲,相似於其餘異步通知。 它們是在事件發生時調度的,但在它們有機會運行以前,必須等待其餘正在運行的腳本完成。
僅當沒有別的事情正在運行時,才能處理事件,這個事實意味着,若是事件循環與其餘工做捆綁在一塊兒,任何頁面交互(經過事件發生)都將延遲,直到有時間處理它爲止。 所以,若是您安排了太多工做,不管是長時間運行的事件處理器仍是大量短期運行的工做,該頁面都會變得緩慢且麻煩。
若是您想在背後作一些耗時的事情而不會凍結頁面,瀏覽器會提供一些名爲 Web Worker 的東西。 Web Worker 是一個 JavaScript 過程,與主腳本一塊兒在本身的時間線上運行。
想象一下,計算一個數字的平方運算是一個重量級的,長期運行的計算,咱們但願在一個單獨的線程中執行。 咱們能夠編寫一個名爲code/squareworker.js
的文件,經過計算平方併發回消息來響應消息:
addEventListener("message", event => { postMessage(event.data * event.data); });
爲了不多線程觸及相同數據的問題,Web Worker 不會將其全局做用域或任何其餘數據與主腳本的環境共享。 相反,你必須經過來回發送消息與他們溝通。
此代碼會生成一個運行該腳本的 Web Worker,向其發送幾條消息並輸出響應。
let squareWorker = new Worker("code/squareworker.js"); squareWorker.addEventListener("message", event => { console.log("The worker responded:", event.data); }); squareWorker.postMessage(10); squareWorker.postMessage(24);
函數postMessage
會發送一條消息,觸發接收方的message
事件。建立工做單元的腳本經過Worker
對象收發消息,而worker
則直接向其全局做用域發送消息,或監聽其消息。只有能夠表示爲 JSON 的值能夠做爲消息發送 - 另外一方將接收它們的副本,而不是值自己。
咱們在第 11 章中看到了setTimeout
函數。 它會在給定的毫秒數以後,調度另外一個函數在稍後調用。
有時讀者須要取消調度的函數。能夠存儲setTimeout
的返回值,並將做爲參數調用clearTimeout
。
let bombTimer = setTimeout(() => { console.log("BOOM!"); }, 500); if (Math.random() < 0.5) { // 50% chance console.log("Defused."); clearTimeout(bombTimer); }
函數cancelAnimationFrame
做用與clearTimeout
相同,使用requestAnimationFrame
的返回值調用該函數,能夠取消幀(假定函數尚未被調用)。
還有setInterval
和clearInterval
這種類似的函數,用於設置計時器,每隔必定毫秒數重複執行一次。
let ticks = 0; let clock = setInterval(() => { console.log("tick", ticks++); if (ticks == 10) { clearInterval(clock); console.log("stop."); } }, 200);
某些類型的事件可能會連續、迅速觸發屢次(例如mousemove
和scroll
事件)。處理這類事件時,你必須當心謹慎,防止處理任務耗時過長,不然處理器會佔據過多事件,致使用戶與文檔交互變得很是慢。
若你須要在這類處理器中編寫一些重要任務,可使用setTimeout
來確保不會頻繁進行這些任務。咱們一般稱之爲「事件降頻(Debounce)」。有許多方法能夠完成該任務。
在第一個示例中,當用戶輸入某些字符時,咱們想要有所反應,但咱們不想在每一個按鍵事件中當即處理該任務。當用戶輸入過快時,咱們但願暫停一下而後進行處理。咱們不是當即在事件處理器中執行動做,而是設置一個定時器。咱們也會清除上一次的定時器(若是有),所以當兩個事件觸發間隔太短(比定時器延時短),就會取消上一次事件設置的定時器。
<textarea>Type something here...</textarea> <script> let textarea = document.querySelector("textarea"); let timeout; textarea.addEventListener("input", () => { clearTimeout(timeout); timeout = setTimeout(() => console.log("Typed!"), 500); }); </script>
將undefined
傳遞給clearTimeout
或在一個已結束的定時器上調用clearTimeout
是沒有效果的。所以,咱們不須要關心什麼時候調用該方法,只須要每一個事件中都這樣作便可。
若是咱們想要保證每次響應之間至少間隔一段時間,但不但願每次事件發生時都重置定時器,而是在一連串事件連續發生時可以定時觸發響應,那麼咱們可使用一個略有區別的方法來解決問題。例如,咱們想要響應"mousemove"
事件來顯示當前鼠標座標,但頻率只有 250ms。
<script> let scheduled = null; window.addEventListener("mousemove", event => { if (!scheduled) { setTimeout(() => { document.body.textContent = `Mouse at ${scheduled.pageX}, ${scheduled.pageY}`; scheduled = null; }, 250); } scheduled = event; }); </script>
事件處理器能夠檢測並響應發生在咱們的 Web 頁面上的事件。addEventListener
方法用於註冊處理器。
每一個事件都有標識事件的類型(keydown
、focus
等)。大多數方法都會在特定 DOM 元素上調用,接着向其父節點傳播,容許每一個父元素的處理器都能處理這些事件。
JavaScript 調用事件處理器時,會傳遞一個包含事件額外信息的事件對象。該對象也有方法支持中止進一步傳播(stopPropagation
),也支持阻止瀏覽器執行事件的默認處理器(preventDefault
)。
按下鍵盤按鍵時會觸發keydown
和keyup
事件。按下鼠標按鈕時,會觸發mousedown
、mouseup
和click
事件。移動鼠標會觸發mousemove
事件。觸摸屏交互會致使"touchstart"
,"touchmove"
和"touchend"
事件。
咱們能夠經過scroll
事件監測滾動行爲,能夠經過focus
和blur
事件監控焦點改變。當文檔完成加載後,會觸發窗口的load
事件。
編寫一個顯示氣球的頁面(使用氣球 emoji,\ud83c\udf88
)。 當你按下上箭頭時,它應該變大(膨脹)10%,而當你按下下箭頭時,它應該縮小(放氣)10%。
您能夠經過在其父元素上設置font-size
CSS 屬性(style.fontSize
)來控制文本大小(emoji 是文本)。 請記住在該值中包含一個單位,例如像素(10px
)。
箭頭鍵的鍵名是"ArrowUp"
和"ArrowDown"
。確保按鍵只更改氣球,而不滾動頁面。
實現了以後,添加一個功能,若是你將氣球吹過必定的尺寸,它就會爆炸。 在這種狀況下,爆炸意味着將其替換爲「爆炸 emoji,\ud83d\udca5
」,而且移除事件處理器(以便您不能使爆炸變大變小)。
<p>💥</p> <script> // Your code here </script>
在 JavaScript 早期,有許多主頁都會在頁面上使用大量的動畫,人們想出了許多該語言的創造性用法。
其中一種是「鼠標蹤影」,也就是一系列的元素,隨着你在頁面上移動鼠標,它會跟着你的鼠標指針。
在本習題中實現鼠標軌跡的功能。使用絕對定位、固定尺寸的<div>
元素,背景爲黑色(請參考鼠標點擊一節中的示例)。建立一系列此類元素,當鼠標移動時,伴隨鼠標指針顯示它們。
有許多方案能夠實現咱們所需的功能。你能夠根據你的須要實現簡單的或複雜的方法。簡單的解決方案是保存固定鼠標的軌跡元素並循環使用它們,每次mousemove
事件觸發時將下一個元素移動到鼠標當前位置。
<style> .trail { /* className for the trail elements */ position: absolute; height: 6px; width: 6px; border-radius: 3px; background: teal; } body { height: 300px; } </style> <script> // Your code here. </script>
選項卡面板普遍用於用戶界面。它支持用戶經過選擇元素上方的不少突出的選項卡來選擇一個面板。
本習題中,你必須實現一個簡單的選項卡界面。編寫asTabs
函數,接受一個 DOM 節點並建立選項卡界面來展示該節點的子元素。該函數應該在頂層節點中插入大量<button>
元素,與每一個子元素一一對應,按鈕文本從子節點的data-tabname
中獲取。除了顯示一個初始子節點,其餘子節點都應該隱藏(將display
樣式設置成none
),並經過點擊按鈕來選擇當前顯示的節點。
當它生效時將其擴展,爲當前選中的選項卡,將按鈕的樣式設爲不一樣的,以便明確選擇了哪一個選項卡。
<tab-panel> <div data-tabname="one">Tab one</div> <div data-tabname="two">Tab two</div> <div data-tabname="three">Tab three</div> </tab-panel> <script> function asTabs(node) { // Your code here. } asTabs(document.querySelector("tab-panel")); </script>