dojo/dom-class源码学习

 dom-class模块是dojo中对于一个元素class特性的操作(特性与属性的区别),主要方法有:

  • contains 判断元素是否包含某个css class
  • add 为元素添加某个css class
  • remove 移除某个css class
  • replace 用某个css class取代另一个css class
  • toggle 开关某个css class

 

  对于支持classList的浏览器可以使用calssList提供的方法,但支持这个属性的浏览器很少,貌似只有firefox和chrome支持。dojo这里使用了通用的方法:更改className的值。

// example:
            //        Add two classes at once:
            //    |    require(["dojo/dom-class"], function(domClass){
            //    |        domClass.add("someNode", "firstClass secondClass");
            //    |    });
            //
            // example:
            //        Add two classes at once (using array):
            //    |    require(["dojo/dom-class"], function(domClass){
            //    |        domClass.add("someNode", ["firstClass", "secondClass"]);
            //    |    });

该模块中的许多方法,比如add、remove、replace既可以添加一个连续的class字符串(类与类之间使用空格相连:"class1 class2 class3")也可以添加class数组。在dojo内部的处理中,全部将“class1 class2 class3”这种形式转化成数组。就是str2Array方法:

var cls, // exports object
        spaces = /\s+/, a1 = [""];

    function str2array(s){
        if(typeof s == "string" || s instanceof String){
            // 单个字符串
            if(s && !spaces.test(s)){
                a1[0] = s;
                return a1;
            }
            var a = s.split(spaces);
            // 去除前面的空白字符,如:“ string”
            if(a.length && !a[0]){
                a.shift();
            }
            // 去除后面的空白字符,如:“string ”
            if(a.length && !a[a.length - 1]){
                a.pop();
            }
            return a;
        }
        // assumed to be an array
        if(!s){
            return [];
        }
        // 普通数组
        return array.filter(s, function(x){ return x; });
    }

 按我的理解去除前后空白字符的过程有些啰嗦,dojo/_base/lang模块有trim方法,就是用来去除前后空白字符。这里完全可以直接调用,看来不是一个人写的。

  

  contains、add、remove这三个函数属于这个模块中的基础方法,理解这个模块的代码还要知道一个核心原理是:这些方法全部为className和新class的头尾新加空格: " class1 class2 class3 "; " newClass ",利用字符串操作的方式来处理,这样既可以提高处理效率又能有效避免浏览器多次重绘引发的性能问题。

  先看一下contains方法:

contains: function containsClass(/*DomNode|String*/ node, /*String*/ classStr){
            // summary:
            //        Returns whether or not the specified classes are a portion of the
            //        class list currently applied to the node.
            // node: String|DOMNode
            //        String ID or DomNode reference to check the class for.
            // classStr: String
            //        A string class name to look for.
            // example:
            //        Do something if a node with id="someNode" has class="aSillyClassName" present
            //    |    if(dojo.hasClass("someNode","aSillyClassName")){ ... }

            return ((" " + dom.byId(node)[className] + " ").indexOf(" " + classStr + " ") >= 0); // Boolean
        },

将className与classStr首尾都添加空格后,利用String类型的indexOf方式来判断是否存在classStr。

add: function addClass(/*DomNode|String*/ node, /*String|Array*/ classStr){

            node = dom.byId(node);
            //转化为数组
            classStr = str2array(classStr);
            var cls = node[className], oldLen;
            // 添加空格字符
            cls = cls ? " " + cls + " " : " ";
            oldLen = cls.length;
            // classStr挨个判断,不存在与cls中的就添加进去
            for(var i = 0, len = classStr.length, c; i < len; ++i){
                c = classStr[i];
                if(c && cls.indexOf(" " + c + " ") < 0){
                    cls += c + " ";
                }
            }
            // cls改变的话就使用新的className
            if(oldLen < cls.length){
                node[className] = cls.substr(1, cls.length - 2);// 去除首尾的空白字符
            }
        }

remove: function removeClass(/*DomNode|String*/ node, /*String|Array?*/ classStr){
            node = dom.byId(node);
            var cls;
            if(classStr !== undefined){
                //这里与add方法中的思路类似
                classStr = str2array(classStr);
                cls = " " + node[className] + " ";
                for(var i = 0, len = classStr.length; i < len; ++i){
                    // 将classStr中的class移除掉
                    cls = cls.replace(" " + classStr[i] + " ", " ");
                }
                cls = lang.trim(cls);
            }else{ // 没有第二个参数则将所有class都移除掉
                cls = "";
            }
            if(node[className] != cls){ node[className] = cls; }
        }

下面介绍replace方法,顾名思义替换,替换的方式通常都是先删除再添加。如果对于同一个节点删除、添加 class会引起浏览器重绘,所以这里引入了fakeNode来降低浏览器重绘次数,提高性能。

replace: function replaceClass(/*DomNode|String*/ node, /*String|Array*/ addClassStr, /*String|Array?*/ removeClassStr){
            node = dom.byId(node);
            //利用fakeNode避免移除、添加过程中浏览器重绘
            fakeNode[className] = node[className];
            cls.remove(fakeNode, removeClassStr);
            cls.add(fakeNode, addClassStr);
            if(node[className] !== fakeNode[className]){
                node[className] = fakeNode[className];
            }
        }

toggle方法可以对一组class进行开关控制,存在则删除,没有则添加。

toggle: function toggleClass(/*DomNode|String*/ node, /*String|Array*/ classStr, /*Boolean?*/ condition){
            node = dom.byId(node);
            if(condition === undefined){
                classStr = str2array(classStr);
                for(var i = 0, len = classStr.length, c; i < len; ++i){
                    c = classStr[i];
                    cls[cls.contains(node, c) ? "remove" : "add"](node, c);
                }
            }else{
                cls[condition ? "add" : "remove"](node, classStr);
            }
            return condition;   // Boolean
        }

看dojo的实现方式,使用toggle对一组class开关操作时会导致浏览器多次重绘,我们完全可以对className和classStr做差异融合,然后一次替换,或者像replace中一样,利用fakeNode来防止多次重绘。

toggle: function toggleClass(/*DomNode|String*/ node, /*String|Array*/ classStr, /*Boolean?*/ condition){
            node = dom.byId(node);
            if(condition === undefined){
                classStr = str2array(classStr);

                fakeNode[className] = node[className];// 利用fakeNode防止多次重绘

                for(var i = 0, len = classStr.length, c; i < len; ++i){
                    c = classStr[i];
                    cls[cls.contains(node, c) ? "remove" : "add"](fakeNode, c);
                }

                // 一次重绘
                if(node[className] !== fakeNode[className]){
                    node[className] = fakeNode[className];
                }
            }else{
                cls[condition ? "add" : "remove"](node, classStr);
            }
            return condition;   // Boolean
        }

如果您觉得这篇文章对您有帮助,请不吝点击右下方“推荐”,谢谢~
时间: 2024-05-20 21:18:34

dojo/dom-class源码学习的相关文章

dojo/_base/lang源码分析

dojo/_base/lang模块是一个工具模块,但几乎用dojo开发的app都会用到这个模块.模块中的方法能够在某些开发场景中避免繁冗的代码,接下来我们一起看看这些工具函数的使用和原理(仅仅是原理的实现,并非是dojo中的源码).   lang.mixin(dest, sources...),这个函数的作用是将所有source中的属性拷贝到dest中,并返回dest.例子如下: var flattened = lang.mixin( { name: "Frylock", braces

STL源码学习——Lists(链表)

STL源码学习--Lists(链表) 今天突然想起来看看开源项目,找了找最后决定好好看看经典的STL喵~ 和STL里的代码比起来我突然觉得以前写的代码也太不规范了喵,估计很多ACMer都一样吧喵. 先从简单的看.先挑了一发list的源码来看.总结如下: 欢迎大家一起讨论喵~ 1 :list是用双向循环链表实现的,就是说 list.end()+1 == list.begin() 2 :list中有一个关键结点,这个结点是 list.end() 3 :在看了list中的erase函数后,发现这个函数

Redis源码学习——BIO

Redis源码学习之BIO BIO顾名思义,background IO,是redis中运行的后台IO. 网上千篇一律的说法是redis是单线程单进程. 实际上redis运行过程中并不是严格单进程单线程应用.Redis中的多进程: 在写入备份(RDB,AOF)的时候,会fork出子进程进行备份文件的写入.Redis中的多线程: AOF的备份模式中,如果我们设置的是AOF_FSYNC_EVERYSEC(每秒备份一次,这个设置可理解为弱同步备份),redis会create一个backgroud线程,在

DotText源码学习——ASP.NET的工作机制

--本文是<项目驱动学习--DotText源码学习>系列的第一篇文章,在这之后会持续发表相关的文章. 概论 在阅读DotText源码之前,让我们首先了解一下ASP.NET的工作机制,可以使我们更好的理解.ASP.NET是Web服务器(IIS)的 ISAPI(Internet Server API)扩展.当IIS接收到客户端浏览器发来的请求后,它根据请求的文件类型确定由哪个ISAPI扩展来处理该请求,并将请求转发给ASP.NET(如 果是ASP.NET处理的相应文件类型的话,如*.aspx.*.

Hadoop源码学习:RPC

Hadoop源码学习:RPC Hadoop RPC使用java NIO编写,达到高性能,轻量级,可控性. 主要分为四层:序列化层,函数调用层,网络传输层,服务器端处理框架 序列化层:实现Writable接口 函数调用层:java反射机制和动态代理实现函数调用 网络传输层:使用Socket机制 服务器端处理框架:基于Reactor设计模式的事件驱动I/O模型 如何使用Hadoop RPC: RPC Server: 1.定义一个协议,实现VersionedProtocol接口, public int

Java集合源码学习(二)ArrayList分析

Java集合源码学习笔记(二)ArrayList分析 1.关于ArrayList ArrayList直接继承AbstractList,实现了List. RandomAccess.Cloneable.Serializable接口, 为什么叫"ArrayList",因为ArrayList内部是用一个数组存储元素值,相当于一个可变大小的数组,也就是动态数组. (1)继承和实现 继承了AbstractList,实现了List:ArrayList是一个数组队列,提供了相关的添加.删除.修改.遍历

有谁写过asp.netsql2000进销存的,我购源码学习?

问题描述 有谁写过asp.netsql2000进销存的,我购源码学习?我最近想学ASP.NET看了很多书,都不行.都差不多.所以想购源码来学习,价格在3000左右,要求商业,但不要求很完善的QQ:421520476 解决方案 解决方案二:但不要求很完善的????商业项目哪个不是比较完善后才出"厂"的,你去51aspx找一下吧,应该有供学习的--

Android源码学习之组合模式定义及应用_Android

组合模式定义: Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. 将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性. 如上图所示(截取自<Head First De

Android源码学习之组合模式定义及应用

组合模式定义: Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. 将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性. 如上图所示(截取自<Head First De

日志分析系统——Hangout源码学习

这两天看了下hangout的代码,虽然没有运行体验过,但是也算是学习了一点皮毛. 架构浅谈 Hangout可以说是java版的Logstash,我是没有测试过性能,不过据说是kafka这边性能要高出Logstash5倍.不知道真的假的,不过看代码,确实要比Logstash高效一点. 关于input,filter,output的关系 在Logstash里面,Input,filter,output是三个独立的部分,每个部分通过Buffer存储数据. 但是Hangout没有采用这种思想,每个Input