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. WebApi Put方法出现MethodNotAllowed解决方法

    WebApi Put方法提交数据的时候总提示MethodNotAllowed,在网上查了很多资料才发现是因为IIS安装了一项服务“WebDAV”,在IIS中把“WebDAV”前面的勾去掉,重新启动电脑 ...

  2. C# 在winform或者wpf中显示控制台窗口

    这儿需要使用两个系统函数: BOOL WINAPI FreeConsole(void); //// 关闭控制台窗口,参考:http://msdn.microsoft.com/en-us/library ...

  3. python:threading多线程模块-创建线程

    创建线程的两种方法: 1,直接调用threading.Thread来构造thread对象,Thread的参数如下: class threading.Thread(group=None, target= ...

  4. CAD 二次开发----- 块(一)

    1.块定义与块参照两个概念 块定义类似于模具,而块参照类似于模具浇筑出来的模型,在图形中只需用块定义来保存块的实际几何组成,而仅用插入点和比例因子来存储块定义,因为块参照的几何形状与快参照完全一样,仅 ...

  5. Coursera Robotics系列课心得

    Robotics Perception Professor Kostas and Jianbo Shi week 1: camera model 凸透镜成像原理:凸透镜焦点与焦距是固定的,这是物理性质 ...

  6. 编译器工具 Flex Bison for Windows 简单入门例子

    最近从事一个系统仿真软件的开发,里面定义了自己的描述性语言MSL, MSL语言经FlexBison转换成C语言,然后用C编译器来编译并计算仿真. 现在领域驱动开发比较热门,有机会定义自己的语言对程序员 ...

  7. Tomcat实现分析(一)--类加载及容器组件

    启动脚本 启动命令中的参数如下: org.apache.catalina.startup.Bootstrap start -Djava.util.logging.config.file=/opt/ap ...

  8. JTA 深度历险 - 原理与实现

    转自http://www.ibm.com/developerworks/cn/java/j-lo-jta/ 在 J2EE 应用中,事务是一个不可或缺的组件模型,它保证了用户操作的 ACID(即原子.一 ...

  9. Windows XP和Word 2007不能正常使用VSTO插件

    今天帮助同事解决了一个小问题,就是在WindowsXP上,为Word2007开发的插件不能正常显示. 通过搜索关键词 WindowsXp Word 2007 VSTO找到了两个解决方案. http:/ ...

  10. SQL SERVER 2000数据库置疑处理

    由于服务器意外的断电,导致SQL SERVER服务器上数据库出现“置疑”而无法使用,通过网上搜索,找到以下方法解决问题,这里记录一下: 产生数据库置疑的时侯,数据库文件和日志文件都是存在的,如果数据库 ...