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 ...
随机推荐
- linux启动执行某个脚本
如果是开机马上执行的脚本,可以将脚本写到rc.local中: 如果是用户登录后自动执行脚本,可以将脚本写到相应的用户目录下“-/.bash_profile”,若脚本“-/.bash_profile”不 ...
- String,StringBuffer
String类代表不可变的字符序列. String s1 = "hello"; String s2 = "hello"; s1 == s2 ==> tr ...
- leetcode解题:Add binary问题
顺便把之前做过的一个简单难度的题也贴上来吧 67. Add Binary Given two binary strings, return their sum (also a binary strin ...
- RGB颜色中的参数是变量的时候,为什么要加上两个+号在左右?
<script> function draw(){ var c=document.getElementById("mycanvas"); var cxt=c.getCo ...
- 如何彻底删除antlr-2.7.2.jar
1 找到Local\MyEclipse\MyEclipse 10\configuration\org.eclipse.osgi\bundles\12\1\.cp\data\1.3\lib,将antlr ...
- Tomcat7下出现The requested resource(/)is not available
1首先确保你的localhost是否正常运行解决方案:1观察项目是否部署2重新将tomcat7导入 2确保你的项目名后跟index.jsp是否正常运行解决方案:1右键项目名,web进行查询,观察部署的 ...
- JS-随机排序
var arr = [ 1,2,3,4,5,6,7,8 ];arr.sort(function ( a, b ) { return Math.random() - 0.5;});alert( a ...
- 远程访问MySQL遇到的一些问题
一.access deny GRANT ALL PRIVILEGES ON *.* TO '用户名'@'IP地址' IDENTIFIED BY '密码' with grant option;
- 解决 IE 6/7 中console对象兼容性问题
话不多说,直接上代码 (function (){ //创建空console对象,避免JS报错 if(!window.console) window.console = {}; var console ...
- Mini projects #6 ---- Blackjack
课程全名:An Introduction to Interactive Programming in Python,来自 Rice University 授课教授:Joe Warren, Scott ...