歡迎來個人專欄查看系列文章。javascript
眼看 jQuery 的源碼就快到頭了,後面還有幾個重要的內容,包括 ajax 和動畫操做,加油把它們看完,百度前端學院的新一批課程也開始了。百度前端學院。css
class 的的操做應該算是比較愉快的,由於內容不是不少,或者說,內容涉及到的原生操做不是很大,就一個 className 或 getAttribute,主要仍是來看下它涉及到的一些兼容性操做。html
先來講一個比較有趣的 class 操做,先把連接貼上。前端
js 有一個很是大的缺陷,就是沒法控制僞元素的樣式,好比 after 和 before,這樣子會失去不少樂趣(一樣也帶來了不少樂趣)。上面的連接是 stackoverflow 的解答。java
1. class 方式node
經過事先定義 class 的方式來解決:git
p:before { content: "c1" } p.click:before { content: "click" } // js $("p").on("click", function(){ $(this).toggleClass('click'); })
2. 內聯 style 方式github
這種方式不優雅,也是一種解決辦法。ajax
var str = "click"; $('<style>p:before{content:"' + str + '""}</style>').appendTo('head');
3. jQuery data-attr 來解決正則表達式
這種方式是依靠 content 的特性:
p:before { content: attr(data-click); } //js var str = 'click'; $("p").on("click", function(){ $(this).attr("data-click", str); })
這種方式應該是動態改變。
jQuery 的應用仍是挺普遍的。
jQuery 中的 class 操做仍是頗有意思,會用到不少正則表達式,我超喜歡正則表達式的。
若是讓我用原生的 js 來實現 class 操做,我會想到兩種方式,一種是 className,它的兼容性很是好,全部瀏覽器都支持,包括 mobile。第二個是 getAttribute,也是全部瀏覽器都支持(有版本限制)。
先從 hasClass 提及吧:
// 獲取 class-name function getClass( elem ) { return elem.getAttribute && elem.getAttribute( "class" ) || ""; } // 將 class name 進行處理 function stripAndCollapse( value ) { var tokens = value.match( /[^\x20\t\r\n\f]+/g ) || []; return tokens.join( " " ); } jQuery.fn.extend( { hasClass: function( selector ) { var className, elem, i = 0; className = " " + selector + " "; while ( ( elem = this[ i++ ] ) ) { if ( elem.nodeType === 1 && ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { return true; } } return false; } } );
能夠看出 getClass
函數使用的是 getAttribute
方法。
接下來看一下添加 add:
jQuery.fn.extend( { addClass: function( value ) { var classes, elem, cur, curValue, clazz, j, finalValue, i = 0; // 參數爲函數... if ( jQuery.isFunction( value ) ) { return this.each( function( j ) { jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); } ); } if ( typeof value === "string" && value ) { // 能夠添加多個 class classes = value.match( /[^\x20\t\r\n\f]+/g ) || []; while ( ( elem = this[ i++ ] ) ) { curValue = getClass( elem ); cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); if ( cur ) { j = 0; while ( ( clazz = classes[ j++ ] ) ) { if ( cur.indexOf( " " + clazz + " " ) < 0 ) { cur += clazz + " "; } } // 在這裏 set class,有個 diff 判斷 finalValue = stripAndCollapse( cur ); // 去除兩側空格 if ( curValue !== finalValue ) { elem.setAttribute( "class", finalValue ); } } } } return this; } } );
jQuery 大體處理的思路是這樣的:先把當前 elem 中的 class 取出來 cur
,要添加的 value
若是在 cur 中 indexOf
的值顯示不存在,就在 cur 後面加上 value。
刪除可能要麻煩一點點:
jQuery.fn.extend( { removeClass: function( value ) { var classes, elem, cur, curValue, clazz, j, finalValue, i = 0; // 不知道在哪裏用到 value 爲 function 狀況 if ( jQuery.isFunction( value ) ) { return this.each( function( j ) { jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); } ); } // 無參數表示 移除全部的 class ... if ( !arguments.length ) { return this.attr( "class", "" ); } if ( typeof value === "string" && value ) { classes = value.match( rnothtmlwhite ) || []; while ( ( elem = this[ i++ ] ) ) { curValue = getClass( elem ); // This expression is here for better compressibility (see addClass) cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); if ( cur ) { j = 0; while ( ( clazz = classes[ j++ ] ) ) { // 移除全部須要移除的 class while ( cur.indexOf( " " + clazz + " " ) > -1 ) { cur = cur.replace( " " + clazz + " ", " " ); } } // Only assign if different to avoid unneeded rendering. finalValue = stripAndCollapse( cur ); if ( curValue !== finalValue ) { elem.setAttribute( "class", finalValue ); } } } } return this; } } );
能夠看出 remove 的操做基本上和 add 同樣,只不過處理 class 的時候略有不一樣:
// 這裏用 while,是有技巧的 while ( cur.indexOf( " " + clazz + " " ) > -1 ) { cur = cur.replace( " " + clazz + " ", " " ); }
用 replace 替換匹配的 clazz 爲空格。
toggleClass 使用的頻率也比較高。
先來看看大體用法,你確定會忽略它的第二個參數的意思。.toggleClass(),當第二個參數爲 true 的狀況,就是 addClass,爲 false 時,removeClass,從源碼來看,就是直接調用的這兩個函數。
除了兩個參數,還有無參和只有 false 狀況,下面也都有明確的處理辦法。
jQuery.fn.extend( { toggleClass: function( value, stateVal ) { var type = typeof value; // 第二個參數爲 boolean if ( typeof stateVal === "boolean" && type === "string" ) { return stateVal ? this.addClass( value ) : this.removeClass( value ); } if ( jQuery.isFunction( value ) ) { return this.each( function( i ) { jQuery( this ).toggleClass( value.call( this, i, getClass( this ), stateVal ), stateVal ); } ); } return this.each( function() { var className, i, self, classNames; if ( type === "string" ) { // Toggle individual class names i = 0; self = jQuery( this ); classNames = value.match( rnothtmlwhite ) || []; while ( ( className = classNames[ i++ ] ) ) { // 有則刪,無則加,邏輯很簡單 if ( self.hasClass( className ) ) { self.removeClass( className ); } else { self.addClass( className ); } } // 當無參或只有一個 false 時,全部 class 都執行 } else if ( value === undefined || type === "boolean" ) { className = getClass( this ); if ( className ) { // Store className if set dataPriv.set( this, "__className__", className ); } if ( this.setAttribute ) { this.setAttribute( "class", className || value === false ? "" : dataPriv.get( this, "__className__" ) || "" ); } } } ); } } );
看得出來,這個邏輯和前面兩個很像,不過當無參或只有一個 boolean 且 false 時,先將當前的 className 保存到 data cache 中,而後實現 toggle 操做:
if ( this.setAttribute ) { this.setAttribute( "class", className || value === false ? // 判斷條件 "" : // 有則設空 dataPriv.get( this, "__className__" ) || "" // 無則從 data cache 取 ); }
感受 jQuery 中的 class 操做不是很複雜,難道是我在進步嗎,哈哈。
jQuery 2.0.3 源碼分析 樣式操做
Selecting and manipulating CSS ..
.toggleClass()
本文在 github 上的源碼地址,歡迎來 star。
歡迎來個人博客交流。