整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          前端進(jìn)階之路:點(diǎn)擊事件綁定

          前端之所以被稱為前端,是因?yàn)樗钦麄€ Web 技術(shù)棧中距離用戶最近、直接與用戶進(jìn)行交互的一環(huán)。而網(wǎng)頁界面與用戶的交互通常是通過各種事件來達(dá)成的;在各種事件之中,點(diǎn)擊事件 往往又是最常見、最通用的一種界面事件。

          本文將介紹我在 “點(diǎn)擊事件綁定” 這一場景下的進(jìn)階之路。

          背景

          我是一個前端小兵,我在一家互聯(lián)網(wǎng)公司做做一些簡單的業(yè)務(wù)開發(fā)。

          某一天,我接到了一個需求,做一個抽獎功能。公司里的前輩們已經(jīng)完成了業(yè)務(wù)邏輯,而且已經(jīng)提供了業(yè)務(wù)功能的接口,只需要我制作頁面并完成事件綁定即可。

          實(shí)踐

          開動

          我寫好了頁面,頁面中有一個 ID 為 lucky-draw 的按鈕元素。接下來,我需要為它綁定點(diǎn)擊事件。我是這樣寫的:

          var btn = document.getElementById('lucky-draw')
          btn.onclick = function () {
          	BX.luckyDraw()
          }
          

          這其中 BX.luckyDraw() 就是前輩們提供的業(yè)務(wù)接口,執(zhí)行它就可以運(yùn)行后續(xù)的抽獎功能。

          我測試了一下,代碼工作正常,于是很開心地準(zhǔn)備上線。

          第一關(guān)

          然而前輩們告訴我,這些重要功能的按鈕是需要加統(tǒng)計的。這也難不倒我,因?yàn)槲液苁煜そy(tǒng)計系統(tǒng)的 API。于是我修改了一下事件綁定的代碼:

          btn.onclick = function () {
          	BX.luckyDraw()
          	BX.track('lucky-draw')
          }
          

          這樣做是有效的,但前輩們又告訴我,因?yàn)槟承┰颍y(tǒng)計代碼和業(yè)務(wù)代碼是分布在不同位置的,以上代碼需要拆開。于是我嘗試這樣修改:

          btn.onclick = function () {
          	BX.luckyDraw()
          }
          // other codes...
          btn.onclick = function () {
          	BX.track('lucky-draw')
          }
          

          結(jié)果發(fā)現(xiàn)點(diǎn)擊按鈕時的抽獎功能失效了。原來,使用 .onclick 這樣的事件屬性來綁定事件有一個非常大的缺點(diǎn),重復(fù)賦值會覆蓋舊值。也就是說,這種方式只能綁定最后一次賦值的事件處理函數(shù)。

          我硬著頭皮去請教前輩,才知道原來這種方式早已經(jīng)不推薦使用了,應(yīng)該使用 DOM 標(biāo)準(zhǔn)的事件綁定 API 來處理(在舊版 IE 下有一些兼容性問題,這里不展開)。因此我的代碼改成了這樣:

          btn.addEventListener('click', function () {
          	BX.luckyDraw()
          }, false)
          // other codes...
          btn.addEventListener('click', function () {
          	BX.track('lucky-draw')
          }, false)
          

          所有功能終于又正常了,我很開心地準(zhǔn)備上線。

          第二關(guān)

          事實(shí)證明我還是太天真了,PM 是不會一次性把所有需求都告訴你的。原來,這個抽獎功能還需要做 A/B 測試,也就是說,只有一半的用戶會看到這個抽獎功能。

          這意味著用戶的頁面上可能根本沒有 btn 這個元素,那么 btn.addEventListener(...) 這一句直接就拋錯了。因此,在為按鈕綁定事件處理函數(shù)之前,我不得不先判斷一下:

          if (btn) {
          	btn.addEventListener('click', function () {
          		BX.luckyDraw()
          	}, false)
          }
          // other codes...
          if (btn) {
          	btn.addEventListener('click', function () {
          		BX.track('lucky-draw')
          	}, false)
          }
          

          雖然這樣的代碼在所有用戶的頁面上都可以正常工作,但這些預(yù)先判斷看起來很蛋疼啊。我再次帶著疑惑向前輩請教。前輩慈祥地看著我,說出了一句經(jīng)典名言:

          傻瓜,為什么不用萬能的 jQuery 呢?

          原來,神奇的 jQuery 允許我們忽略很多細(xì)節(jié),比如這種沒有取到元素的情況會被它默默地消化掉。而且 jQuery 的事件綁定方法也不存在兼容性問題,API 也比較好看。不錯不錯,不管網(wǎng)上的大神們怎么噴 jQuery,但它簡直是我的救星啊!

          于是,我的代碼變成了以下這樣:

          var $btn = $('#lucky-draw')
          $btn.on('click', function () {
          	BX.luckyDraw()
          })
          // other codes...
          $btn.on('click', function () {
          	BX.track('lucky-draw')
          })
          

          我的代碼看起來像那么回事了,我很開心地準(zhǔn)備上線。

          第三關(guān)

          當(dāng)然,我的故事不會這么快結(jié)束。要知道,對一個有追求的前端團(tuán)隊來說,不斷提升用戶體驗(yàn)是永恒的目標(biāo)。比如,我們網(wǎng)站使用了一些方法來提升頁面加載性能,部分頁面內(nèi)容并不是原本存在于頁面中的,而是在用戶需要時,由 JavaScript 動態(tài)生成的。

          拿這個抽獎功能來說,抽獎按鈕存在于一個名為 “驚喜” 的 tab 中,而這個 tab 在初始狀態(tài)下是沒有內(nèi)容的,只有當(dāng)用戶切換到這個 tab 時,才會由 JS 填充其內(nèi)容。示意代碼是這樣的:

          $('.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()
          })
          

          果然,我成功了!好事多磨啊,這個需求終于開心地上線了。

          經(jīng)過進(jìn)一步的研究,我了解到 “事件委托” 的本質(zhì)是利用了事件冒泡的特性。把事件處理函數(shù)綁定到容器元素上,當(dāng)容器內(nèi)的元素觸發(fā)事件時,就會冒泡到容器上。此時可以判斷事件的源頭是誰,再執(zhí)行對應(yīng)的事件處理函數(shù)。由于事件處理函數(shù)是綁定在容器元素上的,即使容器為空也沒有關(guān)系;只要容器的內(nèi)容添加進(jìn)來,整個功能就是準(zhǔn)備就緒的。

          雖然事件委托的原理聽起來稍有些復(fù)雜,但由于 jQuery 對事件委托提供了完善的支持,我的代碼并沒有因此變得很復(fù)雜。

          多想一步

          經(jīng)過這一番磨煉,我收獲了很多經(jīng)驗(yàn)值;同時,我也學(xué)會了更進(jìn)一步去發(fā)現(xiàn)問題和思考問題。比如,在我們的網(wǎng)頁,通常會有多個按鈕,那為它們綁定事件的腳本代碼可能就是這樣的:

          $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 元素多綁定一個事件處理函數(shù);而且根據(jù)直覺,這樣一段段長得差不多的代碼是需要優(yōu)化的。因此,如果我可以把這些類似的代碼整合起來,那不論是在資源消耗方面,還是在代碼組織方面,都是有益的。

          于是,我嘗試把所有這些事件委托的代碼合并為一次綁定。首先,為了實(shí)現(xiàn)合并,我需要為這些按鈕找到共同點(diǎn)。很自然地,我讓它們具有相同的 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 類名的元素被點(diǎn)擊后都會觸發(fā)上面這個事件處理函數(shù)。那么,接下來,我們在這里區(qū)分一下事件源頭,并執(zhí)行對應(yīng)的任務(wù):

          $body.on('click', '.action', function () {
          	switch (this.id) {
          		case 'lucky-draw':
          			BX.luckyDraw()
          			break
          		case 'some-btn':
          			// do something...
          			break
          		// ...
          	}
          })
          

          這樣一來,所有分散的事件委托代碼就被合并為一處了。在這個統(tǒng)一的事件處理函數(shù)中,我們使用 ID 來區(qū)分各個按鈕。

          但 ID 有一些問題,由于同一頁面上不能存在同名的元素,相信前端工程師們都對 ID 比較敏感,在日常開發(fā)中都盡量避免濫用。此外,如果多個按鈕需要執(zhí)行的任務(wù)相同,但它的 ID 又必須不同,則這些 ID 和它們對應(yīng)的任務(wù)之間的對應(yīng)關(guān)系就顯得不夠明確了。

          于是,我改用 HTML5 的自定義屬性來標(biāo)記各個按鈕:

          <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 這個屬性來標(biāo)記各個按鈕元素被點(diǎn)擊時所要執(zhí)行的動作。回過頭看,由于各個按鈕都使用了這個屬性,它們已經(jīng)具備了新的共同點(diǎn),而 class 這個共同點(diǎn)就不必要了,于是我們的 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 代碼也需要做相應(yīng)調(diào)整:

          $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
          		// ...
          	}
          })
          

          我們的代碼看起來已經(jīng)挺不錯了,但我已經(jīng)停不下來了,還要繼續(xù)改進(jìn)。那個長長的 switch 語句看起來有點(diǎn)臃腫。通常優(yōu)化 switch 的方法就是使用對象的鍵名和鍵值來組織這種對應(yīng)關(guān)系。于是我繼續(xù)改:

          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()
          })
          

          經(jīng)過這樣的調(diào)整,我發(fā)現(xiàn)代碼的嵌套變淺了,而且按鈕們的標(biāo)記和它們要做的事情也被組織成了 actionList 這個對象,看起來更清爽了。

          在這樣的組織方式下,如果頁面需要新增一個按鈕,也很容易做擴(kuò)展:

          // HTML
          $body.append('<a href="#" data-action="more-action">Link</a>')
          // JS
          $.extend(actionList, {
          	'more-action': function () {
          		// ...
          	}
          })
          

          到這里,這一整套實(shí)踐終于像那么回事了!

          開源

          我自己用這一套方法參與了很多項(xiàng)目的開發(fā),在處理事件綁定時,它節(jié)省了我很多的精力。我忽然意識到,它可能還適合更多的人、更多的項(xiàng)目。那不妨把它開源吧!

          于是我發(fā)布了 Action 這個項(xiàng)目。這個小巧的類庫幫助開發(fā)者輕松隨意地綁定點(diǎn)擊事件,它使用 “動作” 這個概念來標(biāo)記按鈕和它被點(diǎn)擊后要做的事情;它提供的 API 可以方便地定義一些動作:

          action.add({
          	'my-action': function () {
          		// ...
          	}
          })
          

          也可以手動觸發(fā)已經(jīng)定義的動作:

          action.trigger('my-action')
          

          應(yīng)用

          Action 這個類庫已經(jīng)被移動 Web UI 框架 CMUI 采用,作為全局的基礎(chǔ)服務(wù)。CMUI 內(nèi)部的各個 UI 組件都是基于 Action 的事件綁定機(jī)制來實(shí)現(xiàn)的。我們這里以對話框組件為例,來看看 Action 在 CMUI 中的應(yīng)用(示意代碼):

          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()
          

          只要當(dāng) CMUI.dialog.init() 方法執(zhí)行后,對話框組件就準(zhǔn)備就緒了。我們在業(yè)務(wù)中直接調(diào)用 CMUI.dialog.open() 方法、傳入構(gòu)造對話框所需要的一些配置信息,這個對話框即可創(chuàng)建并打開。

          大家可以發(fā)現(xiàn),在構(gòu)造對話框的過程中,我們沒有做任何事件綁定的工作,對話框的關(guān)閉按鈕就自然具備了點(diǎn)擊關(guān)閉功能!原因就在于關(guān)閉按鈕(<a href="#" data-action="close-dialog">×</a>)自身已經(jīng)通過 data-action 屬性聲明了它被點(diǎn)擊時所要執(zhí)行的動作('close-dialog'),而這個動作早已在組件初始化時(CMUI.dialog.init())定義好了。

          希望本文能幫助到您!

          點(diǎn)贊+轉(zhuǎn)發(fā),讓更多的人也能看到這篇內(nèi)容(收藏不點(diǎn)贊,都是耍流氓-_-)

          關(guān)注 {我},享受文章首發(fā)體驗(yàn)!

          每周重點(diǎn)攻克一個前端技術(shù)難點(diǎn)。更多精彩前端內(nèi)容私信 我 回復(fù)“教程”

          原文鏈接:https://github.com/cssmagic/blog/issues/48

          lt;input>比較重要

          <type>屬性:<text>文本框 <password>密碼框 <checkbox>復(fù)選框 <radio>單選按鈕 <submit> <reset> <file> <hidden> <image> <button>

          默認(rèn)屬性為<text>

          <name>屬性指定表單元素的名稱

          <value>屬性指定表單元素的初始值

          <size>屬性指表單元素的初始寬度 一般用于文本框和密碼框

          <maxlength>屬性指可在<text>或<password>元素中輸入最大的字符串 一般用于文本框和密碼框

          <checked>屬性指此屬性只有一個值,如果不選中就不用添加此屬性 一般用于單選按鈕和復(fù)選框

          <readonly>文本標(biāo)簽指定為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">請輸入自我評價的內(nèi)容···</textarea><br />

          <input type="submit" value="提交"/><br />

          <input type="reset" value="重置"/><br />

          <input type="button" value="普通按鈕" onclick="window.alert('你好’)"/><br />

          </form>

          </body>

          </html>

          讓文本比常規(guī)的字體大一號:

          <p><big>這個文本比較大。</big></p>


          瀏覽器支持

          所有主流瀏覽器都支持 <big> 標(biāo)簽。


          標(biāo)簽定義及使用說明

          HTML5 不支持 <big> 標(biāo)簽。請用 CSS 代替。

          <big> 標(biāo)簽用來制作更大的文本。


          提示和注釋

          提示:在文檔中使用 CSS 來規(guī)定文本大小。


          HTML 4.01 與 HTML5之間的差異

          HTML5 不支持 <big> 標(biāo)簽,HTML 4.01 支持 <big> 標(biāo)簽。


          標(biāo)準(zhǔn)屬性

          在 HTML 4.01 中,<big> 標(biāo)簽支持如下標(biāo)準(zhǔn)屬性:

          屬性描述
          classclassname規(guī)定元素的類名
          dirrtlltr規(guī)定元素中內(nèi)容的文本方向
          idid規(guī)定元素的唯一 id
          langlanguage_code規(guī)定元素中內(nèi)容的語言代碼
          stylestyle_definition規(guī)定元素的行內(nèi)樣式
          titletext規(guī)定元素的額外信息
          xml:langlanguage_code規(guī)定 XHTML 文檔中元素內(nèi)容的語言代碼

          如需完整的描述,請訪問標(biāo)準(zhǔn)屬性。


          事件屬性

          在 HTML 4.01 中,<big> 標(biāo)簽支持如下事件屬性:

          屬性描述
          onclickscript當(dāng)鼠標(biāo)被單擊時執(zhí)行腳本
          ondblclickscript當(dāng)鼠標(biāo)被雙擊時執(zhí)行腳本
          onmousedownscript當(dāng)鼠標(biāo)按鈕被按下時執(zhí)行腳本
          onmousemovescript當(dāng)鼠標(biāo)指針移動時執(zhí)行腳本
          onmouseoutscript當(dāng)鼠標(biāo)指針移出某元素時執(zhí)行腳本
          onmouseoverscript當(dāng)鼠標(biāo)指針懸停于某元素之上時執(zhí)行腳本
          onmouseupscript當(dāng)鼠標(biāo)按鈕被松開時執(zhí)行腳本
          onkeydownscript當(dāng)鍵盤被按下時執(zhí)行腳本
          onkeypressscript當(dāng)鍵盤被按下后又松開時執(zhí)行腳本
          onkeyupscript當(dāng)鍵盤被松開時執(zhí)行腳本

          如需完整的描述,請訪問事件屬性。

          如您還有不明白的可以在下面與我留言或是與我探討QQ群308855039,我們一起飛!


          主站蜘蛛池模板: 国产亚洲综合精品一区二区三区| 日韩精品一区二区三区大桥未久| 亚洲狠狠狠一区二区三区| 日韩免费无码视频一区二区三区 | 伊人色综合一区二区三区| 精品aⅴ一区二区三区| 亚洲成在人天堂一区二区| 亚洲国产韩国一区二区| 怡红院美国分院一区二区| 亚洲毛片不卡av在线播放一区| 成人区精品一区二区不卡| 久久久人妻精品无码一区| 亚洲国产精品一区二区成人片国内| 激情内射亚洲一区二区三区| 呦系列视频一区二区三区| 日韩精品无码久久一区二区三| 人妻少妇精品一区二区三区| 亚洲美女视频一区| 国产成人一区二区动漫精品| 欧洲精品码一区二区三区免费看| 视频一区二区中文字幕| 国产亚洲一区二区三区在线观看 | 交换国产精品视频一区| 色婷婷亚洲一区二区三区| 国产主播一区二区| 色屁屁一区二区三区视频国产| 中文字幕一区二区三区免费视频| 高清精品一区二区三区一区| 亚洲国产成人久久一区WWW| 国产精品一区二区久久精品| 一区二区三区久久精品| 中文乱码人妻系列一区二区| 中文字幕一区二区三区永久| 国产精品av一区二区三区不卡蜜 | 国产香蕉一区二区三区在线视频| 国产在线一区视频| 精品人妻码一区二区三区| 国模丽丽啪啪一区二区| 国产女人乱人伦精品一区二区 | 国产人妖视频一区二区| 日韩伦理一区二区|