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
}
如果您觉得这篇文章对您有帮助,请不吝点击右下方“推荐”,谢谢~
dojo/dom-class源码学习的更多相关文章
- dojo/_base/lang源码分析
dojo/_base/lang模块是一个工具模块,但几乎用dojo开发的app都会用到这个模块.模块中的方法能够在某些开发场景中避免繁冗的代码,接下来我们一起看看这些工具函数的使用和原理(仅仅是原理的 ...
- 【iScroll源码学习04】分离IScroll核心
前言 最近几天我们前前后后基本将iScroll源码学的七七八八了,文章中未涉及的各位就要自己去看了 1. [iScroll源码学习03]iScroll事件机制与滚动条的实现 2. [iScroll源码 ...
- 【iScroll源码学习03】iScroll事件机制与滚动条的实现
前言 想不到又到周末了,周末的时间要抓紧学习才行,前几天我们学习了iScroll几点基础知识: 1. [iScroll源码学习02]分解iScroll三个核心事件点 2. [iScroll源码学习01 ...
- 【iScroll源码学习02】分解iScroll三个核心事件点
前言 最近两天看到很多的总结性发言,我想想今年好像我的变化挺大的,是不是该晚上来水一发呢?嗯,决定了,晚上来水一发! 上周六,我们简单模拟了下iScroll的实现,周日我们开始了学习iScroll的源 ...
- 【iScroll源码学习00】模拟iScroll
前言 相信对移动端有了解的朋友对iScroll这个库非常熟悉吧,今天我们就来说下我们移动页面的iScroll化 iScroll是我们必学框架之一,我们这次先根据iScroll功能自己实现其功能,然后再 ...
- 【iScroll源码学习01】准备阶段 - 叶小钗
[iScroll源码学习01]准备阶段 - 叶小钗 时间 2013-12-29 18:41:00 博客园-原创精华区 原文 http://www.cnblogs.com/yexiaochai/p/3 ...
- 【 js 基础 】【 源码学习 】源码设计 (持续更新)
学习源码,除了学习对一些方法的更加聪明的代码实现,同时也要学习源码的设计,把握整体的架构.(推荐对源码有一定熟悉了之后,再看这篇文章) 目录结构:第一部分:zepto 设计分析第二部分:undersc ...
- colly源码学习
colly源码学习 colly是一个golang写的网络爬虫.它使用起来非常顺手.看了一下它的源码,质量也是非常好的.本文就阅读一下它的源码. 使用示例 func main() { c := coll ...
- Spring源码学习-容器BeanFactory(四) BeanDefinition的创建-自定义标签的解析.md
写在前面 上文Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签对Spring默认标签的解析做了详解,在xml元素的解析中,Spri ...
- Underscore.js 源码学习笔记(下)
上接 Underscore.js 源码学习笔记(上) === 756 行开始 函数部分. var executeBound = function(sourceFunc, boundFunc, cont ...
随机推荐
- vs2015 command prompt here
网上搜的很多方法都不能用,比如:http://app.paraesthesia.com/CommandPromptHere/ 主要是都搞错了注册表路径,写成了: HKCR,Directory\Shel ...
- 2016/10/28 很久没更了 leetcode解题 3sum问题进阶版4sum
18. 4Sum Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c ...
- 关于.NET大数据量大并发量的数据连接池管理
转自:http://www.cnblogs.com/virusswb/archive/2010/01/08/1642055.html 我以前对.NET连接池的认识是错误的,原来以为在web.confi ...
- Android学习 之 ColorStateList按钮文字变色
首先添加一个ColorStateList资源XML文件,XML文件保存在res/color/button_text.xml: <?xml version="1.0" enco ...
- PHP对图片按照一定比例缩放并生成图片文件
list($width, $height)=getimagesize($filename);//缩放比例$per=round(400/$width,3); $n_w=$width*$per;$n_h= ...
- Missing separate debuginfos
问题:Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.192.el6.x86_64解决:1.将/etc/yum.rep ...
- 【线段树】bzoj1018 [SHOI2008]堵塞的交通traffic
线段树的每个叶子节点存一列. 每个节点维护六个域,分别是左上左下.左上右上.左上右下.左下右上.左下右下.右上右下在区间内部的连通性,不考虑绕出去的情况. 初始每个叶子的左上左下.右上右下是连通的. ...
- 将 ASP.NET Core 1.0 应用作为 docker 镜像发布 (Linux版)
var appInsights=window.appInsights||function(config){ function r(config){t[config]=function(){var i= ...
- Python 性能优化——对象绑定
不同函数调用方式对应不同的绑定次数: import profile class A: def f(self): pass def foo(): a = A() for i in range(10000 ...
- pushState 和 replaceState
使用此方法浏览器前进后退仍然可查看对应内容 设置值 history.pushState({}, "页面标题", "xxx.html"); history.rep ...