Skip to content

Commit

Permalink
update and add class style
Browse files Browse the repository at this point in the history
  • Loading branch information
songjinzhong committed Feb 25, 2017
1 parent c912b61 commit 56d890d
Showing 1 changed file with 226 additions and 1 deletion.
227 changes: 226 additions & 1 deletion 18-class/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,233 @@ $("p").on("click", function(){

jQuery 的应用还是挺广泛的。

## fn.hasClass

jQuery 中的 class 操作还是很有意思,会用到很多正则表达式,我超喜欢正则表达式的。

如果让我用原生的 js 来实现 class 操作,我会想到两种方式,一种是 [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className),它的兼容性非常好,所有浏览器都支持,包括 mobile。第二个是 [getAttribute](https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute),也是所有浏览器都支持(有版本限制)。

先从 hasClass 说起吧:

```javascript
// 获取 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` 方法。

## fn.addClass

接下来看一下添加 add:

```javascript
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。

## fn.removeClass

删除可能要麻烦一点点:

```javascript
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 的时候略有不同:

```javascript
// 这里用 while,是有技巧的
while ( cur.indexOf( " " + clazz + " " ) > -1 ) {
cur = cur.replace( " " + clazz + " ", " " );
}
```

用 replace 替换匹配的 clazz 为空格。

## fn.toggleClass

toggleClass 使用的频率也比较高。

先来看看大致用法,你肯定会忽略它的第二个参数的意思。[.toggleClass()](http://www.css88.com/jqapi-1.9/toggleClass/),当第二个参数为 true 的情况,就是 addClass,为 false 时,removeClass,从源码来看,就是直接调用的这两个函数。

```javascript
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++ ] ) ) {

// Check each className given, space separated list
if ( self.hasClass( className ) ) {
self.removeClass( className );
} else {
self.addClass( className );
}
}

// Toggle whole class name
} else if ( value === undefined || type === "boolean" ) {
className = getClass( this );
if ( className ) {

// Store className if set
dataPriv.set( this, "__className__", className );
}

// If the element has a class name or if we're passed `false`,
// then remove the whole classname (if there was one, the above saved it).
// Otherwise bring back whatever was previously saved (if anything),
// falling back to the empty string if nothing was stored.
if ( this.setAttribute ) {
this.setAttribute( "class",
className || value === false ?
"" :
dataPriv.get( this, "__className__" ) || ""
);
}
}
} );
}
} );
```

## 参考

>[jQuery 2.0.3 源码分析 样式操作](http://www.cnblogs.com/aaronjs/p/3433358.html)
>[Selecting and manipulating CSS ..](http://stackoverflow.com/questions/5041494/selecting-and-manipulating-css-pseudo-elements-such-as-before-and-after-usin)
>[Selecting and manipulating CSS ..](http://stackoverflow.com/questions/5041494/selecting-and-manipulating-css-pseudo-elements-such-as-before-and-after-usin)
>[.toggleClass()](http://www.css88.com/jqapi-1.9/toggleClass/)

0 comments on commit 56d890d

Please sign in to comment.