摘要:根據(jù)以上信息,簡單的總結(jié)一下選擇符應(yīng)該是唯一的,不需要添加額外的選擇符。利用這一點(diǎn),可以大大簡化事件的綁定。然而,出于一致性考慮,可以簡單的全部使用方法。事實(shí)上,這種處理完全不必要。我這干脆就簡單起個(gè)小結(jié)吧。
前言
之前一段時(shí)間一直在進(jìn)行舊項(xiàng)目的重構(gòu),發(fā)現(xiàn)了很多問題,系統(tǒng)用的庫是jQuery, 主要是用著方便,其他同事,包括一些外包同事,對這個(gè)庫也比較熟悉 。這里就對一些常見的問題,結(jié)合查到的資料,進(jìn)行一些簡單的總結(jié)。
為什么使用jQuery這個(gè)問題,答案是顯而易見的。還能因?yàn)樯叮?dāng)然是方便唄。沒錯(cuò),它本身就是一個(gè)為dom而生的庫。
jQuery的思想,實(shí)際上很簡單,八個(gè)字可以概括: 選擇元素,進(jìn)行操作。
各類強(qiáng)大的選擇器極大的簡化了dom操作,我們最常使用的也是它的選擇器。
下面就對經(jīng)常使用的一些功能,給出一些建議。
https://jsperf.com/jquery-1-4...
新版本會(huì)改進(jìn)性能,還有很多新功能。
為了直觀的看到版本對性能的影響,我們來看一些測試用例:
以三個(gè)常見的選擇器為例:
$(".elem") $(".elem", context) context.find(".elem")
測試結(jié)果是相同時(shí)間內(nèi) 選擇器的執(zhí)行次數(shù):
可以很明顯的看到,一般來說,新版本比舊版本有顯著的性能提升。
用好選擇器首先列舉幾個(gè)常用的選擇器:
$("#xxx") // ID選擇器 $(".xxx") // 類選擇器 $(input) // 標(biāo)簽選擇器 $(":xxx") // 偽類選擇器 比如 $(":hidden") $("[attribute = value]") //屬性選擇器
使用上面幾個(gè)選擇器 來進(jìn)行測試:
https://jsperf.com/dh-jquery-...
還是先看一下測試數(shù)據(jù):
可以非常直觀的看到,ID選擇器的速度最快,遠(yuǎn)超其他選擇器。
根據(jù)以上信息,簡單的總結(jié)一下:
1. ID選擇符應(yīng)該是唯一的,不需要添加額外的選擇符。 不推薦的: $("div#id"), $("#a #id"), $("div#id span.class") 正確的姿勢: $("#id") ,$("#id .class") 2. 避免使用隱式通用選擇符 不推薦的: $(".class :radio") 正確的姿勢: $(".class input:radio") 3. 避免使用通用選擇器`*` , 性能很差。 不推薦的: $(".container > *") 正確的姿勢: $(".container").children()
遵循以上規(guī)則, 選擇器的使用上基本就沒什么問題了,之后,我們還可以繼續(xù)優(yōu)化。
利用緩存頻繁操作dom浪費(fèi)性能,所以我們可以把重復(fù)使用的的元素緩存起來。
// 在變量前加$前綴,便于識(shí)別出jQuery對象 var $a = $("#a"), $container = $("#container"), $container_item = $container.find("li"), // 利用緩存,比 $("#container li") 好一些 // ... //如果你想更方便的管理,也可以使用對象字面量的方式: var DOM = { $a : $("#a"), $container : $("#container"), $container_item : $container.find("li"), // ... } 后面使用的時(shí)候,就直接可以使用我們定義的DOM對象了。
DOM.$a.on("click",function(){ //xxx })
選擇器也好了,下面我們看dom的操作。
鏈?zhǔn)讲僮?/b>采用鏈?zhǔn)綄懛ǎ琷Query自動(dòng)緩存每一步的結(jié)果,因此比非鏈?zhǔn)綄懛ㄒ臁?/p>
$("div").find("span").eq(2).html("Hello World"); // 如果操作很多,會(huì)導(dǎo)致鏈很長,這時(shí)可以添加適當(dāng)?shù)膿Q行來 增加可讀性 : $("div") .find("span") .eq(2) .html("Hello World"); 同樣適用于一些樣式的操作: $target.on("click",function(){ $target.css({ "border":"1px solid #ffffd", "color":"#fff", // ... }) })非空判斷
如果要進(jìn)行非空判斷 或者 未定義判斷的時(shí)候 使用 === 而不要使用==.
如果你使用jsHint ,會(huì)有這樣的提示:
// Use "===" to compare with "null" // Use "===" to compare with "" // Use "===" to compare with "undefined" 在分支判斷的時(shí)候, 可以直接利用其本身的含義就可以了: if ( $something !== null) { // xxx } // 更好的方式: if( something) { // xxx } 同樣的,還有其他類似的判斷,比如長度。 if(list.length > 0 ){ // xxx } 就可以寫成 if(list.length){ // xxx }事件的委托處理
javascript的事件模型,采用"冒泡"模式,也就是說,子元素的事件會(huì)逐級向上"冒泡",成為父元素的事件。利用這一點(diǎn),可以大大簡化事件的綁定。
比如,有一個(gè)表格(table元素),里面有100個(gè)格子(td元素),現(xiàn)在要求在每個(gè)格子上面綁定一個(gè)點(diǎn)擊事件(click):
$("td").on("click", function(){ $(this).toggleClass("active"); });
這種做法是不推薦的,,因?yàn)閠d元素發(fā)生點(diǎn)擊事件之后,這個(gè)事件會(huì)"冒泡"到父元素table上面,從而被監(jiān)聽到。所以,這個(gè)事件只需要在父元素綁定1次即可。
$("table").on("click", "td", function(){ $(this).toggleClass("active"); }); //更好的寫法,則是把事件綁定在document對象上面。 $(document).on("click", "td", function(){ $(this).toggleClass("active"); }); 如果要取消事件的綁定,就使用off()方法。 $(document).off("click", "td");使用"on"
在新版jQuery中,更短的 on(“click”) 用來取代類似 click() 這樣的函數(shù)。 在之前的版本中 on() 就是 bind()。 自從jQuery 1.7版本后,on() 附加事件處理程序的首選方法。 然而,出于一致性考慮,可以簡單的全部使用 on()方法。
$("#id").click(function(){ //xxx }); 替換為: $("#id").on("click", function(){ //xxx })使用短路求值
短路求值是一個(gè)從左到右求值的表達(dá)式,用 && 或 || 操作符。
if(!$value){ $value = $("#id").val(); } 可以寫成: $value = $value || $("#id").val(); //也可以用 || 來填充默認(rèn)值 var age = myAge || 18 ;避免過度使用jQuery
每當(dāng)你使用一次選擇器(比如$("#id")),就會(huì)生成一個(gè)jQuery對象。jQuery對象是一個(gè)很龐大的對象,帶有很多屬性和方法,會(huì)占用不少資源。所以,盡量少生成jQuery對象。
以最簡單的選擇器為例:
document.getElementById("foo") 就要比 $("#foo") 快很多。
再來看一個(gè)例子:
$("a").on ("click" ,function(){ alert($(this).attr("id")); }) //點(diǎn)擊a元素后,彈出該元素的id屬性。 //為了獲取這個(gè)屬性,必須連續(xù)兩次調(diào)用jQuery,第一次是$(this),第二次是attr("id")。 //事實(shí)上,這種處理完全不必要。更正確的寫法是,直接采用javascript原生方法,調(diào)用this.id: $("a").on ("click" ,function(){ alert(this.id); })
上面簡單說了一些簡單的優(yōu)化寫法,我們再看看如何更好的組織你的邏輯
邏輯組織如果遇到抄起鍵盤就開干的小伙伴,沒有組織邏輯,那寫的代碼可能是這樣的:
var a = xxx; var b = xxx; $("#id").on("click", function(){ // xxx }) $(".class").on("click", function(){ // xxx }) function xxx() { // xxx } // 直到最后
如果整個(gè)頁面頁面的邏輯比較復(fù)雜,那到最后的代碼,估計(jì)只有你自己清楚是怎么走的,別人要去看,要不斷的查找你的寫的各種方法,簡直痛苦。
這里就說一種 或許是更好的方式。
下面就給個(gè)實(shí)際的例子吧:
/** * Created by jf on 2015/9/11. * Modified by bear on 2016/9/7. */ $(function () { var pageManager = { $container: $("#container"), _pageStack: [], _configs: [], _pageAppend: function(){}, _defaultPage: null, _pageIndex: 1, setDefault: function (defaultPage) { this._defaultPage = this._find("name", defaultPage); return this; }, setPageAppend: function (pageAppend) { this._pageAppend = pageAppend; return this; }, init: function () { var self = this; $(window).on("hashchange", function () { var state = history.state || {}; var url = location.hash.indexOf("#") === 0 ? location.hash : "#"; var page = self._find("url", url) || self._defaultPage; if (state._pageIndex <= self._pageIndex || self._findInStack(url)) { self._back(page); } else { self._go(page); } }); if (history.state && history.state._pageIndex) { this._pageIndex = history.state._pageIndex; } this._pageIndex--; var url = location.hash.indexOf("#") === 0 ? location.hash : "#"; var page = self._find("url", url) || self._defaultPage; this._go(page); return this; }, push: function (config) { this._configs.push(config); return this; }, go: function (to) { var config = this._find("name", to); if (!config) { return; } location.hash = config.url; }, _go: function (config) { this._pageIndex ++; history.replaceState && history.replaceState({_pageIndex: this._pageIndex}, "", location.href); var html = $(config.template).html(); var $html = $(html).addClass("slideIn").addClass(config.name); $html.on("animationend webkitAnimationEnd", function(){ $html.removeClass("slideIn").addClass("js_show"); }); this.$container.append($html); this._pageAppend.call(this, $html); this._pageStack.push({ config: config, dom: $html }); if (!config.isBind) { this._bind(config); } return this; }, back: function () { history.back(); }, _back: function (config) { this._pageIndex --; var stack = this._pageStack.pop(); if (!stack) { return; } var url = location.hash.indexOf("#") === 0 ? location.hash : "#"; var found = this._findInStack(url); if (!found) { var html = $(config.template).html(); var $html = $(html).addClass("js_show").addClass(config.name); $html.insertBefore(stack.dom); if (!config.isBind) { this._bind(config); } this._pageStack.push({ config: config, dom: $html }); } stack.dom.addClass("slideOut").on("animationend webkitAnimationEnd", function () { stack.dom.remove(); }); return this; }, _findInStack: function (url) { var found = null; for(var i = 0, len = this._pageStack.length; i < len; i++){ var stack = this._pageStack[i]; if (stack.config.url === url) { found = stack; break; } } return found; }, _find: function (key, value) { var page = null; for (var i = 0, len = this._configs.length; i < len; i++) { if (this._configs[i][key] === value) { page = this._configs[i]; break; } } return page; }, _bind: function (page) { var events = page.events || {}; for (var t in events) { for (var type in events[t]) { this.$container.on(type, t, events[t][type]); } } page.isBind = true; } }; function fastClick(){ var supportTouch = function(){ try { document.createEvent("TouchEvent"); return true; } catch (e) { return false; } }(); var _old$On = $.fn.on; $.fn.on = function(){ if(/click/.test(arguments[0]) && typeof arguments[1] == "function" && supportTouch){ // 只擴(kuò)展支持touch的當(dāng)前元素的click事件 var touchStartY, callback = arguments[1]; _old$On.apply(this, ["touchstart", function(e){ touchStartY = e.changedTouches[0].clientY; }]); _old$On.apply(this, ["touchend", function(e){ if (Math.abs(e.changedTouches[0].clientY - touchStartY) > 10) return; e.preventDefault(); callback.apply(this, [e]); }]); }else{ _old$On.apply(this, arguments); } return this; }; } function preload(){ $(window).on("load", function(){ var imgList = [ "./images/layers/content.png", "./images/layers/navigation.png", "./images/layers/popout.png", "./images/layers/transparent.gif" ]; for (var i = 0, len = imgList.length; i < len; ++i) { new Image().src = imgList[i]; } }); } function androidInputBugFix(){ // .container 設(shè)置了 overflow 屬性, 導(dǎo)致 Android 手機(jī)下輸入框獲取焦點(diǎn)時(shí), 輸入法擋住輸入框的 bug // 相關(guān) issue: https://github.com/weui/weui/issues/15 // 解決方法: // 0. .container 去掉 overflow 屬性, 但此 demo 下會(huì)引發(fā)別的問題 // 1. 參考 http://stackoverflow.com/questions/23757345/android-does-not-correctly-scroll-on-input-focus-if-not-body-element // Android 手機(jī)下, input 或 textarea 元素聚焦時(shí), 主動(dòng)滾一把 if (/Android/gi.test(navigator.userAgent)) { window.addEventListener("resize", function () { if (document.activeElement.tagName == "INPUT" || document.activeElement.tagName == "TEXTAREA") { window.setTimeout(function () { document.activeElement.scrollIntoViewIfNeeded(); }, 0); } }) } } function init(){ preload(); fastClick(); androidInputBugFix(); window.pageManager = pageManager; window.home = function(){ location.hash = ""; }; } init(); });
如果想使用另一種更直觀的方式,可以接著往下看。
另一種組織方式var Module = function() { this.init(); }; // 初始化 Module.prototype.init = function() { this.fetchData(function() { // do something }); }; // 綁定事件 Module.prototype.bindEvent = function() { // ... }; // 獲取數(shù)據(jù) Module.prototype.fetchData = function(cb) { var self = this; ajax({}).then(function(data) { self.renderData(data); }).catch(function() { self._fetchDataFailed(); }).fin(function() { cb && cb(); }); }; // 渲染數(shù)據(jù) Module.prototype.renderData = function(data) { data = this._resolveData(data); // ... this.bindEvent(); }; // 處理數(shù)據(jù) Module.prototype._resolveData = function() { // ... }; // 加載失敗 Module.prototype._fetchDataFailed = function() { // ... };
當(dāng)然,你也可以將這兩種形式混合起來用,具體怎么用,就看個(gè)人習(xí)慣了。
這里僅僅介紹一些方法,拋磚引玉。如果你覺得這種方式不好,也可以提出來,以供大家參考學(xué)習(xí)。
結(jié)語啰哩啰嗦 終于到頭了..
類似的這種博文,簡單搜一下就有很多,各種 指南 ,最佳實(shí)踐。我這干脆就簡單起個(gè)小結(jié)吧。
說到底,這篇文字就當(dāng)是對前幾天做的事情的一個(gè)簡單總結(jié),結(jié)合其他資料, 寫出來的一點(diǎn)東西。
回頭看看 其實(shí)也沒什么。
查資料,寫出來 的過程其實(shí)也是一個(gè)自我學(xué)習(xí)的過程,這大概就是寫博客的意義所在吧。
不多說了,還有很多需求要做,都排到四月去了 (容我做個(gè)悲傷的表情)。就到這里吧。
如果想了解更多類似的內(nèi)容,可以查看這兩篇:
https://segmentfault.com/p/12...
http://www.ruanyifeng.com/blo...
以上 :-)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/81803.html
摘要:最近在項(xiàng)目中使用到設(shè)置,使用場景是頁面上有三類單選框,一個(gè)是全選所有頁數(shù)據(jù)一個(gè)是選擇當(dāng)前頁,一個(gè)是選擇一條,也算常用的場景。的屬性在頁面首次加載時(shí)就確定。最后,總結(jié)下獲取和設(shè)置屬性的方法。 最近在項(xiàng)目中使用到j(luò)Query設(shè)置checkbox,使用場景是頁面上有三類單選框,一個(gè)是全選所有頁數(shù)據(jù)(id=cb1),一個(gè)是選擇當(dāng)前頁(id=cb2),一個(gè)是選擇一條(name=cb3),也算常用...
摘要:前言得益于金三銀四,在最近一段時(shí)間,面試了一些人,但是符合的寥寥無幾。看到我的面試題自己寫的面試題,自己想的答案。聽人說過一個(gè)面試套路面試官問的問題,可能面試官自己都不懂,目的只是為了壓工資,挫士氣。不過我是為了測試面試者是不是真的精通。 技術(shù)在不斷的創(chuàng)新,隨著框架,庫,構(gòu)建工具,打包工具,版本控制工具等操作越來越方便,使用越來越簡單。面對這樣的情況,除了興奮,也要警惕。這些工具使得開...
摘要:一選擇器部分選擇不同的選擇被選中的選擇父類元素僅限于直接父類元素只要是父類元素即可,能向父級多級查找選擇子類元素選擇兄弟元素二插入和刪除元素插入在被選元素的結(jié)尾插入內(nèi)容請選擇在被選元素的開頭插入內(nèi)容在被選元素之前插入內(nèi)容在被選元素之后插入內(nèi) 一、選擇器部分 $(input[type=radio]) 選擇不同 type 的 input $(input[type=radio]:ch...
摘要:所以在此次開發(fā)中,嘗試了小步快跑快速迭代的方法。開發(fā),不僅是在開發(fā)運(yùn)用上的提升,還是一個(gè)開源項(xiàng)目的完整實(shí)踐。由于時(shí)間原因,在開發(fā)完基礎(chǔ)版本后就去做別的項(xiàng)目。所以,好的文檔是項(xiàng)目的開門鑰匙。兩個(gè)項(xiàng)目相輔相成。 showImg(https://segmentfault.com/img/bVba47g?w=900&h=150); 歡迎交換友鏈: laker.me--進(jìn)擊的程序媛Github:...
閱讀 2203·2021-10-18 13:28
閱讀 2512·2021-10-11 10:59
閱讀 2340·2019-08-29 15:06
閱讀 1132·2019-08-26 13:54
閱讀 808·2019-08-26 13:52
閱讀 3149·2019-08-26 12:02
閱讀 2999·2019-08-26 11:44
閱讀 2512·2019-08-26 10:56