摘要:根據以上信息,簡單的總結一下選擇符應該是唯一的,不需要添加額外的選擇符。利用這一點,可以大大簡化事件的綁定。然而,出于一致性考慮,可以簡單的全部使用方法。事實上,這種處理完全不必要。我這干脆就簡單起個小結吧。
前言
之前一段時間一直在進行舊項目的重構,發現了很多問題,系統用的庫是jQuery, 主要是用著方便,其他同事,包括一些外包同事,對這個庫也比較熟悉 。這里就對一些常見的問題,結合查到的資料,進行一些簡單的總結。
為什么使用jQuery這個問題,答案是顯而易見的。還能因為啥,當然是方便唄。沒錯,它本身就是一個為dom而生的庫。
jQuery的思想,實際上很簡單,八個字可以概括: 選擇元素,進行操作。
各類強大的選擇器極大的簡化了dom操作,我們最常使用的也是它的選擇器。
下面就對經常使用的一些功能,給出一些建議。
https://jsperf.com/jquery-1-4...
新版本會改進性能,還有很多新功能。
為了直觀的看到版本對性能的影響,我們來看一些測試用例:
以三個常見的選擇器為例:
$(".elem") $(".elem", context) context.find(".elem")
測試結果是相同時間內 選擇器的執行次數:
可以很明顯的看到,一般來說,新版本比舊版本有顯著的性能提升。
用好選擇器首先列舉幾個常用的選擇器:
$("#xxx") // ID選擇器 $(".xxx") // 類選擇器 $(input) // 標簽選擇器 $(":xxx") // 偽類選擇器 比如 $(":hidden") $("[attribute = value]") //屬性選擇器
使用上面幾個選擇器 來進行測試:
https://jsperf.com/dh-jquery-...
還是先看一下測試數據:
可以非常直觀的看到,ID選擇器的速度最快,遠超其他選擇器。
根據以上信息,簡單的總結一下:
1. ID選擇符應該是唯一的,不需要添加額外的選擇符。 不推薦的: $("div#id"), $("#a #id"), $("div#id span.class") 正確的姿勢: $("#id") ,$("#id .class") 2. 避免使用隱式通用選擇符 不推薦的: $(".class :radio") 正確的姿勢: $(".class input:radio") 3. 避免使用通用選擇器`*` , 性能很差。 不推薦的: $(".container > *") 正確的姿勢: $(".container").children()
遵循以上規則, 選擇器的使用上基本就沒什么問題了,之后,我們還可以繼續優化。
利用緩存頻繁操作dom浪費性能,所以我們可以把重復使用的的元素緩存起來。
// 在變量前加$前綴,便于識別出jQuery對象 var $a = $("#a"), $container = $("#container"), $container_item = $container.find("li"), // 利用緩存,比 $("#container li") 好一些 // ... //如果你想更方便的管理,也可以使用對象字面量的方式: var DOM = { $a : $("#a"), $container : $("#container"), $container_item : $container.find("li"), // ... } 后面使用的時候,就直接可以使用我們定義的DOM對象了。
DOM.$a.on("click",function(){ //xxx })
選擇器也好了,下面我們看dom的操作。
鏈式操作采用鏈式寫法,jQuery自動緩存每一步的結果,因此比非鏈式寫法要快。
$("div").find("span").eq(2).html("Hello World"); // 如果操作很多,會導致鏈很長,這時可以添加適當的換行來 增加可讀性 : $("div") .find("span") .eq(2) .html("Hello World"); 同樣適用于一些樣式的操作: $target.on("click",function(){ $target.css({ "border":"1px solid #ffffd", "color":"#fff", // ... }) })非空判斷
如果要進行非空判斷 或者 未定義判斷的時候 使用 === 而不要使用==.
如果你使用jsHint ,會有這樣的提示:
// Use "===" to compare with "null" // Use "===" to compare with "" // Use "===" to compare with "undefined" 在分支判斷的時候, 可以直接利用其本身的含義就可以了: if ( $something !== null) { // xxx } // 更好的方式: if( something) { // xxx } 同樣的,還有其他類似的判斷,比如長度。 if(list.length > 0 ){ // xxx } 就可以寫成 if(list.length){ // xxx }事件的委托處理
javascript的事件模型,采用"冒泡"模式,也就是說,子元素的事件會逐級向上"冒泡",成為父元素的事件。利用這一點,可以大大簡化事件的綁定。
比如,有一個表格(table元素),里面有100個格子(td元素),現在要求在每個格子上面綁定一個點擊事件(click):
$("td").on("click", function(){ $(this).toggleClass("active"); });
這種做法是不推薦的,,因為td元素發生點擊事件之后,這個事件會"冒泡"到父元素table上面,從而被監聽到。所以,這個事件只需要在父元素綁定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() 這樣的函數。 在之前的版本中 on() 就是 bind()。 自從jQuery 1.7版本后,on() 附加事件處理程序的首選方法。 然而,出于一致性考慮,可以簡單的全部使用 on()方法。
$("#id").click(function(){ //xxx }); 替換為: $("#id").on("click", function(){ //xxx })使用短路求值
短路求值是一個從左到右求值的表達式,用 && 或 || 操作符。
if(!$value){ $value = $("#id").val(); } 可以寫成: $value = $value || $("#id").val(); //也可以用 || 來填充默認值 var age = myAge || 18 ;避免過度使用jQuery
每當你使用一次選擇器(比如$("#id")),就會生成一個jQuery對象。jQuery對象是一個很龐大的對象,帶有很多屬性和方法,會占用不少資源。所以,盡量少生成jQuery對象。
以最簡單的選擇器為例:
document.getElementById("foo") 就要比 $("#foo") 快很多。
再來看一個例子:
$("a").on ("click" ,function(){ alert($(this).attr("id")); }) //點擊a元素后,彈出該元素的id屬性。 //為了獲取這個屬性,必須連續兩次調用jQuery,第一次是$(this),第二次是attr("id")。 //事實上,這種處理完全不必要。更正確的寫法是,直接采用javascript原生方法,調用this.id: $("a").on ("click" ,function(){ alert(this.id); })
上面簡單說了一些簡單的優化寫法,我們再看看如何更好的組織你的邏輯
邏輯組織如果遇到抄起鍵盤就開干的小伙伴,沒有組織邏輯,那寫的代碼可能是這樣的:
var a = xxx; var b = xxx; $("#id").on("click", function(){ // xxx }) $(".class").on("click", function(){ // xxx }) function xxx() { // xxx } // 直到最后
如果整個頁面頁面的邏輯比較復雜,那到最后的代碼,估計只有你自己清楚是怎么走的,別人要去看,要不斷的查找你的寫的各種方法,簡直痛苦。
這里就說一種 或許是更好的方式。
下面就給個實際的例子吧:
/** * 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){ // 只擴展支持touch的當前元素的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 設置了 overflow 屬性, 導致 Android 手機下輸入框獲取焦點時, 輸入法擋住輸入框的 bug // 相關 issue: https://github.com/weui/weui/issues/15 // 解決方法: // 0. .container 去掉 overflow 屬性, 但此 demo 下會引發別的問題 // 1. 參考 http://stackoverflow.com/questions/23757345/android-does-not-correctly-scroll-on-input-focus-if-not-body-element // Android 手機下, input 或 textarea 元素聚焦時, 主動滾一把 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() { // ... }; // 獲取數據 Module.prototype.fetchData = function(cb) { var self = this; ajax({}).then(function(data) { self.renderData(data); }).catch(function() { self._fetchDataFailed(); }).fin(function() { cb && cb(); }); }; // 渲染數據 Module.prototype.renderData = function(data) { data = this._resolveData(data); // ... this.bindEvent(); }; // 處理數據 Module.prototype._resolveData = function() { // ... }; // 加載失敗 Module.prototype._fetchDataFailed = function() { // ... };
當然,你也可以將這兩種形式混合起來用,具體怎么用,就看個人習慣了。
這里僅僅介紹一些方法,拋磚引玉。如果你覺得這種方式不好,也可以提出來,以供大家參考學習。
結語啰哩啰嗦 終于到頭了..
類似的這種博文,簡單搜一下就有很多,各種 指南 ,最佳實踐。我這干脆就簡單起個小結吧。
說到底,這篇文字就當是對前幾天做的事情的一個簡單總結,結合其他資料, 寫出來的一點東西。
回頭看看 其實也沒什么。
查資料,寫出來 的過程其實也是一個自我學習的過程,這大概就是寫博客的意義所在吧。
不多說了,還有很多需求要做,都排到四月去了 (容我做個悲傷的表情)。就到這里吧。
如果想了解更多類似的內容,可以查看這兩篇:
https://segmentfault.com/p/12...
http://www.ruanyifeng.com/blo...
以上 :-)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/81803.html
摘要:最近在項目中使用到設置,使用場景是頁面上有三類單選框,一個是全選所有頁數據一個是選擇當前頁,一個是選擇一條,也算常用的場景。的屬性在頁面首次加載時就確定。最后,總結下獲取和設置屬性的方法。 最近在項目中使用到jQuery設置checkbox,使用場景是頁面上有三類單選框,一個是全選所有頁數據(id=cb1),一個是選擇當前頁(id=cb2),一個是選擇一條(name=cb3),也算常用...
摘要:前言得益于金三銀四,在最近一段時間,面試了一些人,但是符合的寥寥無幾。看到我的面試題自己寫的面試題,自己想的答案。聽人說過一個面試套路面試官問的問題,可能面試官自己都不懂,目的只是為了壓工資,挫士氣。不過我是為了測試面試者是不是真的精通。 技術在不斷的創新,隨著框架,庫,構建工具,打包工具,版本控制工具等操作越來越方便,使用越來越簡單。面對這樣的情況,除了興奮,也要警惕。這些工具使得開...
摘要:一選擇器部分選擇不同的選擇被選中的選擇父類元素僅限于直接父類元素只要是父類元素即可,能向父級多級查找選擇子類元素選擇兄弟元素二插入和刪除元素插入在被選元素的結尾插入內容請選擇在被選元素的開頭插入內容在被選元素之前插入內容在被選元素之后插入內 一、選擇器部分 $(input[type=radio]) 選擇不同 type 的 input $(input[type=radio]:ch...
摘要:所以在此次開發中,嘗試了小步快跑快速迭代的方法。開發,不僅是在開發運用上的提升,還是一個開源項目的完整實踐。由于時間原因,在開發完基礎版本后就去做別的項目。所以,好的文檔是項目的開門鑰匙。兩個項目相輔相成。 showImg(https://segmentfault.com/img/bVba47g?w=900&h=150); 歡迎交換友鏈: laker.me--進擊的程序媛Github:...
閱讀 2211·2021-10-18 13:28
閱讀 2523·2021-10-11 10:59
閱讀 2347·2019-08-29 15:06
閱讀 1140·2019-08-26 13:54
閱讀 817·2019-08-26 13:52
閱讀 3153·2019-08-26 12:02
閱讀 3008·2019-08-26 11:44
閱讀 2519·2019-08-26 10:56