Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
前端之所以被稱為前端,是因為它是整個 Web 技術棧中距離用戶最近、直接與用戶進行交互的一環。而網頁界面與用戶的交互通常是通過各種事件來達成的;在各種事件之中,點擊事件 往往又是最常見、最通用的一種界面事件。
本文將介紹我在 “點擊事件綁定” 這一場景下的進階之路。
我是一個前端小兵,我在一家互聯網公司做做一些簡單的業務開發。
某一天,我接到了一個需求,做一個抽獎功能。公司里的前輩們已經完成了業務邏輯,而且已經提供了業務功能的接口,只需要我制作頁面并完成事件綁定即可。
開動
我寫好了頁面,頁面中有一個 ID 為 lucky-draw 的按鈕元素。接下來,我需要為它綁定點擊事件。我是這樣寫的:
var btn = document.getElementById('lucky-draw') btn.onclick = function () { BX.luckyDraw() }
這其中 BX.luckyDraw() 就是前輩們提供的業務接口,執行它就可以運行后續的抽獎功能。
我測試了一下,代碼工作正常,于是很開心地準備上線。
第一關
然而前輩們告訴我,這些重要功能的按鈕是需要加統計的。這也難不倒我,因為我很熟悉統計系統的 API。于是我修改了一下事件綁定的代碼:
btn.onclick = function () { BX.luckyDraw() BX.track('lucky-draw') }
這樣做是有效的,但前輩們又告訴我,因為某些原因,統計代碼和業務代碼是分布在不同位置的,以上代碼需要拆開。于是我嘗試這樣修改:
btn.onclick = function () { BX.luckyDraw() } // other codes... btn.onclick = function () { BX.track('lucky-draw') }
結果發現點擊按鈕時的抽獎功能失效了。原來,使用 .onclick 這樣的事件屬性來綁定事件有一個非常大的缺點,重復賦值會覆蓋舊值。也就是說,這種方式只能綁定最后一次賦值的事件處理函數。
我硬著頭皮去請教前輩,才知道原來這種方式早已經不推薦使用了,應該使用 DOM 標準的事件綁定 API 來處理(在舊版 IE 下有一些兼容性問題,這里不展開)。因此我的代碼改成了這樣:
btn.addEventListener('click', function () { BX.luckyDraw() }, false) // other codes... btn.addEventListener('click', function () { BX.track('lucky-draw') }, false)
所有功能終于又正常了,我很開心地準備上線。
第二關
事實證明我還是太天真了,PM 是不會一次性把所有需求都告訴你的。原來,這個抽獎功能還需要做 A/B 測試,也就是說,只有一半的用戶會看到這個抽獎功能。
這意味著用戶的頁面上可能根本沒有 btn 這個元素,那么 btn.addEventListener(...) 這一句直接就拋錯了。因此,在為按鈕綁定事件處理函數之前,我不得不先判斷一下:
if (btn) { btn.addEventListener('click', function () { BX.luckyDraw() }, false) } // other codes... if (btn) { btn.addEventListener('click', function () { BX.track('lucky-draw') }, false) }
雖然這樣的代碼在所有用戶的頁面上都可以正常工作,但這些預先判斷看起來很蛋疼啊。我再次帶著疑惑向前輩請教。前輩慈祥地看著我,說出了一句經典名言:
傻瓜,為什么不用萬能的 jQuery 呢?
原來,神奇的 jQuery 允許我們忽略很多細節,比如這種沒有取到元素的情況會被它默默地消化掉。而且 jQuery 的事件綁定方法也不存在兼容性問題,API 也比較好看。不錯不錯,不管網上的大神們怎么噴 jQuery,但它簡直是我的救星啊!
于是,我的代碼變成了以下這樣:
var $btn = $('#lucky-draw') $btn.on('click', function () { BX.luckyDraw() }) // other codes... $btn.on('click', function () { BX.track('lucky-draw') })
我的代碼看起來像那么回事了,我很開心地準備上線。
第三關
當然,我的故事不會這么快結束。要知道,對一個有追求的前端團隊來說,不斷提升用戶體驗是永恒的目標。比如,我們網站使用了一些方法來提升頁面加載性能,部分頁面內容并不是原本存在于頁面中的,而是在用戶需要時,由 JavaScript 動態生成的。
拿這個抽獎功能來說,抽獎按鈕存在于一個名為 “驚喜” 的 tab 中,而這個 tab 在初始狀態下是沒有內容的,只有當用戶切換到這個 tab 時,才會由 JS 填充其內容。示意代碼是這樣的:
$('.tabs > .surprise').on('click', function () { var htmlSurpriseTab = [ '<div>', '<button id="lucky-draw">Lucky Draw</button>', '</div>' ].join('') $('.tab-panels > .surprise').html(htmlSurpriseTab) // BTN READY })
這意味著,我寫的事件綁定代碼需要寫在 // BTN READY 處。這種深層的耦合看起來很不理想,我需要想辦法解決它。
我想起來,我在閱讀 jQuery 文檔時看到有一種叫作 “事件委托” 的方法,可以在元素還未添加到頁面之前就為它綁定事件。于是,我嘗試這樣來寫:
$(document.body).on('click', '#lucky-draw', function () { BX.luckyDraw() })
果然,我成功了!好事多磨啊,這個需求終于開心地上線了。
經過進一步的研究,我了解到 “事件委托” 的本質是利用了事件冒泡的特性。把事件處理函數綁定到容器元素上,當容器內的元素觸發事件時,就會冒泡到容器上。此時可以判斷事件的源頭是誰,再執行對應的事件處理函數。由于事件處理函數是綁定在容器元素上的,即使容器為空也沒有關系;只要容器的內容添加進來,整個功能就是準備就緒的。
雖然事件委托的原理聽起來稍有些復雜,但由于 jQuery 對事件委托提供了完善的支持,我的代碼并沒有因此變得很復雜。
多想一步
經過這一番磨煉,我收獲了很多經驗值;同時,我也學會了更進一步去發現問題和思考問題。比如,在我們的網頁,通常會有多個按鈕,那為它們綁定事件的腳本代碼可能就是這樣的:
$body = $(document.body) $body.on('click', '#lucky-draw', function () { BX.luckyDraw() }) $body.on('click', '#some-btn', function () { // do something... }) $body.on('click', '#another-btn', function () { // do something else... })
我隱隱覺得這樣不對勁啊!雖然這些代碼可以正常工作,但每多一個按鈕就要為 body 元素多綁定一個事件處理函數;而且根據直覺,這樣一段段長得差不多的代碼是需要優化的。因此,如果我可以把這些類似的代碼整合起來,那不論是在資源消耗方面,還是在代碼組織方面,都是有益的。
于是,我嘗試把所有這些事件委托的代碼合并為一次綁定。首先,為了實現合并,我需要為這些按鈕找到共同點。很自然地,我讓它們具有相同的 class:
<button class="action" id="lucky-draw">Lucky Draw</button> <button class="action" id="some-action">Button</button> <a href="#" class="action" id="another-action">Link</a> <a href="#" class="action" id="another-action-2">Link</a>
然后,我試圖通過一次事件委托來處理所有這些按鈕:
$body.on('click', '.action', function () { // WHEN CLICK ANY '.action', WE COME HERE. })
很顯然,所有具有 action 類名的元素被點擊后都會觸發上面這個事件處理函數。那么,接下來,我們在這里區分一下事件源頭,并執行對應的任務:
$body.on('click', '.action', function () { switch (this.id) { case 'lucky-draw': BX.luckyDraw() break case 'some-btn': // do something... break // ... } })
這樣一來,所有分散的事件委托代碼就被合并為一處了。在這個統一的事件處理函數中,我們使用 ID 來區分各個按鈕。
但 ID 有一些問題,由于同一頁面上不能存在同名的元素,相信前端工程師們都對 ID 比較敏感,在日常開發中都盡量避免濫用。此外,如果多個按鈕需要執行的任務相同,但它的 ID 又必須不同,則這些 ID 和它們對應的任務之間的對應關系就顯得不夠明確了。
于是,我改用 HTML5 的自定義屬性來標記各個按鈕:
<button class="action" data-action="lucky-draw">Lucky Draw</button> <button class="action" data-action="some-action">Button</button> <a href="#" class="action" data-action="another-action">Link</a> <a href="#" class="action" data-action="another-action-2">Link</a>
我在這里使用了 data-action 這個屬性來標記各個按鈕元素被點擊時所要執行的動作。回過頭看,由于各個按鈕都使用了這個屬性,它們已經具備了新的共同點,而 class 這個共同點就不必要了,于是我們的 HTML 代碼可以簡化一些:
<button data-action="lucky-draw">Lucky Draw</button> <button data-action="some-action">Button</button> <a href="#" data-action="another-action">Link</a> <a href="#" data-action="another-action-2">Link</a>
同時 JS 代碼也需要做相應調整:
$body.on('click', '[data-action]', function () { var actionName = $(this).data('action') switch (actionName) { case 'lucky-draw': BX.luckyDraw() break case 'some-btn': // do something... break // ... } })
我們的代碼看起來已經挺不錯了,但我已經停不下來了,還要繼續改進。那個長長的 switch 語句看起來有點臃腫。通常優化 switch 的方法就是使用對象的鍵名和鍵值來組織這種對應關系。于是我繼續改:
var actionList = { 'lucky-draw': function () { BX.luckyDraw() }, 'some-btn': function () { // do something... } // ... } $body.on('click', '[data-action]', function () { var actionName = $(this).data('action') var action = actionList[actionName] if ($.isFunction(action)) action() })
經過這樣的調整,我發現代碼的嵌套變淺了,而且按鈕們的標記和它們要做的事情也被組織成了 actionList 這個對象,看起來更清爽了。
在這樣的組織方式下,如果頁面需要新增一個按鈕,也很容易做擴展:
// HTML $body.append('<a href="#" data-action="more-action">Link</a>') // JS $.extend(actionList, { 'more-action': function () { // ... } })
到這里,這一整套實踐終于像那么回事了!
開源
我自己用這一套方法參與了很多項目的開發,在處理事件綁定時,它節省了我很多的精力。我忽然意識到,它可能還適合更多的人、更多的項目。那不妨把它開源吧!
于是我發布了 Action 這個項目。這個小巧的類庫幫助開發者輕松隨意地綁定點擊事件,它使用 “動作” 這個概念來標記按鈕和它被點擊后要做的事情;它提供的 API 可以方便地定義一些動作:
action.add({ 'my-action': function () { // ... } })
也可以手動觸發已經定義的動作:
action.trigger('my-action')
應用
Action 這個類庫已經被移動 Web UI 框架 CMUI 采用,作為全局的基礎服務。CMUI 內部的各個 UI 組件都是基于 Action 的事件綁定機制來實現的。我們這里以對話框組件為例,來看看 Action 在 CMUI 中的應用(示意代碼):
CMUI.dialog = { template: [ '<div class="dialog">', '<a href="#" data-action="close-dialog">×</a>', '<h2><%= data.title %></h2>', '<div class="content"><%- data.html %></div>', '</div>' ].join(''), init: function () { action.add({ 'close-dialog': function () { $(this).closest('.dialog').hide() } }) }, open: function (config) { var html = render(this.template, config) $(html).appendTo('body').show() } } CMUI.dialog.init()
只要當 CMUI.dialog.init() 方法執行后,對話框組件就準備就緒了。我們在業務中直接調用 CMUI.dialog.open() 方法、傳入構造對話框所需要的一些配置信息,這個對話框即可創建并打開。
大家可以發現,在構造對話框的過程中,我們沒有做任何事件綁定的工作,對話框的關閉按鈕就自然具備了點擊關閉功能!原因就在于關閉按鈕(<a href="#" data-action="close-dialog">×</a>)自身已經通過 data-action 屬性聲明了它被點擊時所要執行的動作('close-dialog'),而這個動作早已在組件初始化時(CMUI.dialog.init())定義好了。
點贊+轉發,讓更多的人也能看到這篇內容(收藏不點贊,都是耍流氓-_-)
關注 {我},享受文章首發體驗!
每周重點攻克一個前端技術難點。更多精彩前端內容私信 我 回復“教程”
原文鏈接:https://github.com/cssmagic/blog/issues/48
lt;input>比較重要
<type>屬性:<text>文本框 <password>密碼框 <checkbox>復選框 <radio>單選按鈕 <submit> <reset> <file> <hidden> <image> <button>
默認屬性為<text>
<name>屬性指定表單元素的名稱
<value>屬性指定表單元素的初始值
<size>屬性指表單元素的初始寬度 一般用于文本框和密碼框
<maxlength>屬性指可在<text>或<password>元素中輸入最大的字符串 一般用于文本框和密碼框
<checked>屬性指此屬性只有一個值,如果不選中就不用添加此屬性 一般用于單選按鈕和復選框
<readonly>文本標簽指定為readonly文本不允許更改 一般用于文本框和密碼框
多行文本域<textarea > <cols>寬度 <rows>高度
<submit>提交按鈕
<reset>重置按鈕
<button>普通按鈕
!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<form name="myfoem" action="" meathod="post">
用戶id<input type="text" name="userid" size="25" value="用戶id自動生成" readonly="readonly"/><br />
密碼<input type="password" name="realname"/><br />
自我評價:<textarea name="msg" cols="40" rows="5">請輸入自我評價的內容···</textarea><br />
<input type="submit" value="提交"/><br />
<input type="reset" value="重置"/><br />
<input type="button" value="普通按鈕" onclick="window.alert('你好’)"/><br />
</form>
</body>
</html>
例
讓文本比常規的字體大一號:
<p><big>這個文本比較大。</big></p>
瀏覽器支持
所有主流瀏覽器都支持 <big> 標簽。
標簽定義及使用說明
HTML5 不支持 <big> 標簽。請用 CSS 代替。
<big> 標簽用來制作更大的文本。
提示和注釋
提示:在文檔中使用 CSS 來規定文本大小。
HTML 4.01 與 HTML5之間的差異
HTML5 不支持 <big> 標簽,HTML 4.01 支持 <big> 標簽。
標準屬性
在 HTML 4.01 中,<big> 標簽支持如下標準屬性:
屬性 | 值 | 描述 |
---|---|---|
class | classname | 規定元素的類名 |
dir | rtlltr | 規定元素中內容的文本方向 |
id | id | 規定元素的唯一 id |
lang | language_code | 規定元素中內容的語言代碼 |
style | style_definition | 規定元素的行內樣式 |
title | text | 規定元素的額外信息 |
xml:lang | language_code | 規定 XHTML 文檔中元素內容的語言代碼 |
如需完整的描述,請訪問標準屬性。
事件屬性
在 HTML 4.01 中,<big> 標簽支持如下事件屬性:
屬性 | 值 | 描述 |
---|---|---|
onclick | script | 當鼠標被單擊時執行腳本 |
ondblclick | script | 當鼠標被雙擊時執行腳本 |
onmousedown | script | 當鼠標按鈕被按下時執行腳本 |
onmousemove | script | 當鼠標指針移動時執行腳本 |
onmouseout | script | 當鼠標指針移出某元素時執行腳本 |
onmouseover | script | 當鼠標指針懸停于某元素之上時執行腳本 |
onmouseup | script | 當鼠標按鈕被松開時執行腳本 |
onkeydown | script | 當鍵盤被按下時執行腳本 |
onkeypress | script | 當鍵盤被按下后又松開時執行腳本 |
onkeyup | script | 當鍵盤被松開時執行腳本 |
如需完整的描述,請訪問事件屬性。
如您還有不明白的可以在下面與我留言或是與我探討QQ群308855039,我們一起飛!
*請認真填寫需求信息,我們會在24小時內與您取得聯系。