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
}
如果您觉得这篇文章对您有帮助,请不吝点击右下方“推荐”,谢谢~

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

  1. dojo/_base/lang源码分析

    dojo/_base/lang模块是一个工具模块,但几乎用dojo开发的app都会用到这个模块.模块中的方法能够在某些开发场景中避免繁冗的代码,接下来我们一起看看这些工具函数的使用和原理(仅仅是原理的 ...

  2. 【iScroll源码学习04】分离IScroll核心

    前言 最近几天我们前前后后基本将iScroll源码学的七七八八了,文章中未涉及的各位就要自己去看了 1. [iScroll源码学习03]iScroll事件机制与滚动条的实现 2. [iScroll源码 ...

  3. 【iScroll源码学习03】iScroll事件机制与滚动条的实现

    前言 想不到又到周末了,周末的时间要抓紧学习才行,前几天我们学习了iScroll几点基础知识: 1. [iScroll源码学习02]分解iScroll三个核心事件点 2. [iScroll源码学习01 ...

  4. 【iScroll源码学习02】分解iScroll三个核心事件点

    前言 最近两天看到很多的总结性发言,我想想今年好像我的变化挺大的,是不是该晚上来水一发呢?嗯,决定了,晚上来水一发! 上周六,我们简单模拟了下iScroll的实现,周日我们开始了学习iScroll的源 ...

  5. 【iScroll源码学习00】模拟iScroll

    前言 相信对移动端有了解的朋友对iScroll这个库非常熟悉吧,今天我们就来说下我们移动页面的iScroll化 iScroll是我们必学框架之一,我们这次先根据iScroll功能自己实现其功能,然后再 ...

  6. 【iScroll源码学习01】准备阶段 - 叶小钗

    [iScroll源码学习01]准备阶段 - 叶小钗 时间 2013-12-29 18:41:00 博客园-原创精华区 原文  http://www.cnblogs.com/yexiaochai/p/3 ...

  7. 【 js 基础 】【 源码学习 】源码设计 (持续更新)

    学习源码,除了学习对一些方法的更加聪明的代码实现,同时也要学习源码的设计,把握整体的架构.(推荐对源码有一定熟悉了之后,再看这篇文章) 目录结构:第一部分:zepto 设计分析第二部分:undersc ...

  8. colly源码学习

    colly源码学习 colly是一个golang写的网络爬虫.它使用起来非常顺手.看了一下它的源码,质量也是非常好的.本文就阅读一下它的源码. 使用示例 func main() { c := coll ...

  9. Spring源码学习-容器BeanFactory(四) BeanDefinition的创建-自定义标签的解析.md

    写在前面 上文Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签对Spring默认标签的解析做了详解,在xml元素的解析中,Spri ...

  10. Underscore.js 源码学习笔记(下)

    上接 Underscore.js 源码学习笔记(上) === 756 行开始 函数部分. var executeBound = function(sourceFunc, boundFunc, cont ...

随机推荐

  1. 使用afinal下载文件并且在状态栏中显示下载的进度

    2013年10月23日,今天是在“我在找你信息服务有限公司”第一天上班,公司给提出了这样一个要求:下载本公司的app,并且在下载的过程中要在状态栏中显示下载的进度,并且,可以暂停和继续下载. 下面是我 ...

  2. C#数据结构选择

    选择一个合适的数据结构会对程序的性能有着显著的提高 线性表和链表: 1.LinkedList<T>:适合于元素数组不固定,存在大量列表的头尾添加动作场合.其它可使用List<T> ...

  3. MFC字符串转化成16进制

    //CString m_str = _T("11"); //USES_CONVERSION; //char *m_cc = T2A(m_str); //BYTE m_bb; //s ...

  4. Mac Virtual System On Windows

    Win8.1下利用虚拟机安装苹果操作系统 所需文件: 虚拟机:VMware -10.0.1,这个就是中文版的了. 虚拟机密钥生成器:vm10keygen,要对应虚拟机的版本. 虚拟机的插件: unlo ...

  5. Hadoop2.6.0的FileInputFormat的任务切分原理分析(即如何控制FileInputFormat的map任务数量)

    前言 首先确保已经搭建好Hadoop集群环境,可以参考<Linux下Hadoop集群环境的搭建>一文的内容.我在测试mapreduce任务时,发现相比于使用Job.setNumReduce ...

  6. (Python)继承

    面向对象的另一个特性是继承,继承可以更好的代码重用. 例如一个学校里面的成员有老师.学生.老师和学生都有共同的属性名字和年纪.但老师还有它自己的属性,如工资.学生也有它的属性,如成绩. 因此我们可以设 ...

  7. SQl server 关于重复插入数据的测试

    最近发布的脚本,有那种防止重复插入数据(包括存在时更新,不存在是插入的处理,判断的方向可能与下面的示例相反) 使用类似下面的 SQL declare @id int, @value int if no ...

  8. html5 canvas用动画的形式装载图像

    本示例使用HTML5 canvas,简单的编写了装载图片效果, 请使用支持HTML5的浏览器预览效果: 下图为以逐渐横向栅格的效果图 html部分: <!DOCTYPE html> < ...

  9. <%%>标签 什么意思

    <%%>是说这里面的文本不是普通直接输出到客户端的文本,而是需要服务器来解释的.不光是可以写JAVA脚本,这要看具体服务器端使用的是什么技术ASP/JSP/PHP/ASP.NET都使用相同 ...

  10. unity 实现物体破碎效果的一些方法

    游戏越来越接近现实的感觉,如果有一个真是的 虚拟现实设备,可能我们真的会感觉是在真实世界.场景的逼真是在渲染效果.角色AI.游戏逻辑.物理效果等等一起导致的结果.现在游戏越来越大,除了渲染,物理估计是 ...