首页 > 网站 > WEB开发 > 正文

jQuery.attributes源码分析(attr/prop/val/class)

2024-04-27 14:20:25
字体:
来源:转载
供稿:网友

jQuery.attributes源码分析(attr/PRop/val/class)

回顾

有了之前的几篇对于jQuery.attributes相关的研究,是时候分析jQuery.attr的源码了

  • javascript中的attribute和property分析
  • attribute和property兼容性分析
  • jQuery.access源码分析

结构

jQuery.fn.extend({    attr: function (name, value) {    },    removeAttr: function (name) {    },    prop: function (name, value) {         },    removeProp: function (name) {    },    hasClass: function () {},    addClass:function () {},    toggleClass:function () {},    val: function () {}});jQuery.extend({     attr: function () {    },    removeAttr: function () {    },    prop: function () {    },});

关于jQuery的hasClass/addClass/removeClass/toggleClass

  • 1.承袭jQuery一贯的风格,对函数的重载做的相当不错,不仅可以传入单个类名,而且可以传入用空格分隔的类型,传入函数
  • 2.addClass思路:
        1.判断value是否为函数,如果是,遍历jQuery内部所有元素,传入函数返回值,jQuery.addClass之
        2.将传入的className进行处理,双层循环,一层循环所有元素,一层循环所有类名
        3.添加className时进行判断,如果当前元素的className为空,直接赋值,如果不为空,判断当前需要插入的类名是否存在,如果不存在,拼接进入缓存类名字符串
        4.将缓存类名字符串设置进入元素的className
  • 3.removeClass与addClass如出一辙,删除前依旧进行判断是否存在
  • 4.hasClass:将元素的当前类名取出,用字符串的indexOf方法进行判断是否存在
  • 5.toggleClass:
        1.处理传入函数的可能
        2.遍历jQuery所有元素,如果value值是个字符串,遍历所有类名,用addClass和removeClass进行状态转换
        3.如果type没有传入,或者type是字符串,判断是否value为false,如果是,就将className设置为空,否则,将className从缓存系统里取出来,设置回去
jQuery.fn.extend({    /**     * 向被选元素添加一个或多个类     * @param value 'aclass' 'aclass bclass dclass',function () {}     */    addClass: function (value) {        var classes, elem, cur, clazz, j,            i = 0,            len = this.length,            proceed = typeof value === "string" && value; //检测value是否为字符串        /**         * $('#box').addClass(function (elem,oldClassName) {         *      return 'm-general-abc';         * });         *         * 如果是个函数,那么逐个遍历现有元素,递归addClass方法         */        if (jQuery.isFunction(value)) {            return this.each(function (j) {                jQuery(this).addClass(value.call(this, j, this.className));            });        }        // 如果是个字符串,那就执行正真的添加        if (proceed) {            // The disjunction here is for better compressibility (see removeClass)            classes = ( value || "" ).match(core_rnotwhite) || []; //将value用空格分开成一个数组 classes = value.split(//s+/);            for (; i < len; i++) { //遍历所有的元素                elem = this[ i ];                cur = elem.nodeType === 1 && ( elem.className ? //检测是否为HTMLElement                    ( " " + elem.className + " " ).replace(rclass, " ") : //去掉换行什么的,两边加上空格,防止出错                    " " //如果没有class的话,那就等于一个空格                    );                if (cur) {                    j = 0;                    while ((clazz = classes[j++])) { //遍历所有的classes                        if (cur.indexOf(" " + clazz + " ") < 0) { //如果没有的话,才加入,如有,跳出了就                            cur += clazz + " ";                        }                    }                    elem.className = jQuery.trim(cur); //设置className,并且trim一下                }            }        }        // 链式结构,返回被封装的元素        return this;    },    removeClass: function (value) {        var classes, elem, cur, clazz, j,            i = 0,            len = this.length,            proceed = arguments.length === 0 || typeof value === "string" && value;        if (jQuery.isFunction(value)) {            return this.each(function (j) {                jQuery(this).removeClass(value.call(this, j, this.className));            });        }        if (proceed) {            classes = ( value || "" ).match(core_rnotwhite) || [];            for (; i < len; i++) {                elem = this[ i ];                // This expression is here for better compressibility (see addClass)                cur = elem.nodeType === 1 && ( elem.className ?                    ( " " + elem.className + " " ).replace(rclass, " ") : //去掉换行什么的,两边加上空格,防止出错                    ""                    );                if (cur) {                    j = 0;                    while ((clazz = classes[j++])) {                        // Remove *all* instances                        while (cur.indexOf(" " + clazz + " ") >= 0) {                            cur = cur.replace(" " + clazz + " ", " "); //如果存在,就删除                        }                    }                    elem.className = value ? jQuery.trim(cur) : ""; //重新设置,如果没了,就设为空                }            }        }        // 链式结构,返回被封装的元素        return this;    },    /**     * 设置或移除被选元素的一个或多个类进行切换     * 该方法检查每个元素中指定的类。如果不存在则添加类,如果已设置则删除之。这就是所谓的切换效果。     * @param value String:类名  Function:规定返回需要添加或删除的一个或多个类名的函数$(selector).toggleClass(function(index,class,switch),switch)     * @param stateVal 规定是否添加(true)或移除(false)类 为true不存在,则添加.为false,已存在,则删除     * @returns {*}     */    toggleClass: function (value, stateVal) {        var type = typeof value,            isBool = typeof stateVal === "boolean";        /**         * $('#box').toggleClass(function (elem,oldClassName,stateVal) {         *      return 'm-general-abc';         * });         */        if (jQuery.isFunction(value)) {            return this.each(function (i) {                jQuery(this).toggleClass(value.call(this, i, this.className, stateVal), stateVal);            });        }        return this.each(function () {            if (type === "string") {                // toggle individual class names                var className,                    i = 0,                    self = jQuery(this),                    state = stateVal,                    classNames = value.match(core_rnotwhite) || [];                while ((className = classNames[ i++ ])) { //遍历所有的classNames                    // check each className given, space separated list                    //如果stateVal是布尔值,那么就去state,如果不是,就看hasClass是否有                    //按照逻辑,执行添加或者删除class函数                    state = isBool ? state : !self.hasClass(className);                    self[ state ? "addClass" : "removeClass" ](className);                }                // Toggle whole class name                // 如果没有传入type或者type是个布尔值,那么就取当前DOM元素的className属性,用缓存系统将__className__设置成当前的className                //            } else if (type === core_strundefined || type === "boolean") {                if (this.className) {                    // store className if set                    jQuery._data(this, "__className__", this.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.                // 这里就判断是否value为false,如果是,就将className设置为空,否则,将className从缓存系统里取出来,设置回去                this.className = this.className || value === false ? "" : jQuery._data(this, "__className__") || "";            }        });    },    /**     * 检查被选元素是否包含指定的 class     * @param selector selector 类名     * @returns {boolean} 返回true表示包含,返回false,表示未包含     */    hasClass: function (selector) {        var className = " " + selector + " ",            i = 0,            l = this.length;        for (; i < l; i++) {            if (this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf(className) >= 0) {                return true;            }        }        return false;    }});

关于jQuery.attr和jQuery.prop

  • 1.依赖jQuery.access保证传参的灵活性
  • 2.对于很多种意外情况进行判断过滤(注释节点、xml等、falsh没有getAttribute等)
  • 3.运用钩子机制,解决浏览器的兼容问题,为了兼容IE6-8确实做了不少钩子,也够辛苦,也保证了可扩展性,很明显,jQuery 2.x却没有那么多的钩子的兼容
jQuery.fn.extend({    attr: function (name, value) {        return jQuery.access(this, jQuery.attr, name, value, arguments.length > 1);    },    removeAttr: function (name) {        return this.each(function () {            jQuery.removeAttr(this, name);        });    },    prop: function (name, value) {        return jQuery.access(this, jQuery.prop, name, value, arguments.length > 1);    },    removeProp: function (name) {
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表