dojo/dom模块作为一个基础模块,最常用的就是byId方法。除此之外还有isDescendant和setSelectable方法。

  dom.byId(myId)方法:

    各种前端类库都免不了与DOM节点打交道,操作DOM的方法千变万化最终还是要回到原生的那几个方法中,因为类库再快也快不过原生。所以在dom.byId方法中,还是要依靠document.getElementById('myId')方法。假如没有ie,假如不要考虑兼容性,getElementById方法可以完全满足我们的需求。但是,ie毁了一切,借用美国同事的一句话:Fuck the stupid IE! 兼容性问题有两条:

  • ie8及较低版本中,myId不区分大小写,所以myid跟myId会返回同样的结果
  • ie7及较低版本中,如果name名称与给定ID相同的表单元素且表单元素在给定ID元素的前面,那么IE就会返回那个表单元素

  这就要求我们在必须判断一下得到的元素的id是否真与传入参数相同。判断方法是利用id属性或id特性节点:

var te = id && document.getElementById(id)
te && (te.attributes.id.value == id || te.id == id)

如果上帝为你关上了一扇门,他一定会为你打开另一扇门(好矫情,就行天无绝人之路嘛)。ie4开始提供了document.all,它是一个代表所有元素的集合。document.all[myId]返回id为myId的一个元素或者包含name为myId的一个类数组。我们可以循环判断其中的元素是否满足要求:

var eles = document.all[id];if(!eles || eles.nodeName){eles = [eles];}// if more than 1, choose first with the correct idvar i = 0;while((te = eles[i++])){if((te.attributes && te.attributes.id && te.attributes.id.value == id) || te.id == id){return te;}}

所以,dojo/dom中的实现根据浏览器的不同,有不同的实现:

if(has("ie")){dom.byId = function(id, doc){if(typeof id != "string"){return id;}var _d = doc || win.doc, te = id && _d.getElementById(id);// attributes.id.value is better than just id in case the// user has a name=id inside a formif(te && (te.attributes.id.value == id || te.id == id)){return te;}else{var eles = _d.all[id];if(!eles || eles.nodeName){eles = [eles];}// if more than 1, choose first with the correct idvar i = 0;while((te = eles[i++])){if((te.attributes && te.attributes.id && te.attributes.id.value == id) || te.id == id){return te;}}}};}else{dom.byId = function(id, doc){// inline'd type check.// be sure to return null per documentation, to match IE branch.return ((typeof id == "string") ? (doc || win.doc).getElementById(id) : id) || null; // DOMNode};}

  dom.isDescendant(node, ancestor)方法:

  这个方法用来判断node是否是ancestor的一个子节点,其实就是孩子找父亲。孩子找父亲比较简单,而父亲找孩子是比较难的,因为子节点一定有父节点,所以只要一级一级的找上去即可。

dom.isDescendant = function(/*DOMNode|String*/ node, /*DOMNode|String*/ ancestor){try{node = dom.byId(node);ancestor = dom.byId(ancestor);while(node){if(node == ancestor){return true; // Boolean}node = node.parentNode;}}catch(e){ /* squelch, return false */ }return false; // Boolean};

其实还有一个原生的函数也可以满足要求:element.contains方法,不过这个方法并没有被纳入规范中。但是几乎所有的浏览器都支持,包括IE(最初就是ie增加的该方法,总算做了件好事。。)。所以该方法也可以这样实现:

dom.isDescendant = function(/*DOMNode|String*/ node, /*DOMNode|String*/ ancestor){try{node = dom.byId(node);ancestor = dom.byId(ancestor);return ancestor.contains(node);}catch(e){ /* squelch, return false */ }return false; // Boolean};

 dom.setSelectable(node, selectable)方法:

  看名字也知道是用来设置一个节点及其自己点是否可选中的。css属性中可以通过设置“user-select”来控制一个元素是否可选择,但这个属性并未被纳入标准中去,所以各个浏览器中都需要加浏览器前缀,如:-webkit、-moz、-ms、-o等;所以我们可以通过设置元素的style属性中的相应属性来控制元素的可选择性。但是,ie总是太操蛋,大多数情况下,ms前缀都可以解决问题,但是如果一个将一个frame作为编辑器使用时,设置msUserSelect为none时无法达到效果,所以在ie中我们利用unselectable特性来解决这个问题。ie下存在的这个特性:unselectable, 设为on则不可选中,移除这个属性则表示可选中。

  dojo的实现中,首先判断userSelect属性是否能使用:

has.add("css-user-select", function(global, doc, element){// Avoid exception when dom.js is loaded in non-browser environmentsif(!element){ return false; }var style = element.style;var prefixes = ["Khtml", "O", "Moz", "Webkit"],i = prefixes.length,name = "userSelect",prefix;// Iterate prefixes from most to least likelydo{if(typeof style[name] !== "undefined"){// Supported; return property namereturn name;}}while(i-- && (name = prefixes[i] + "UserSelect"));// Not supported if we didn't return before nowreturn false;});

  这里省略了ms前缀。

  然后根据对"css-user-select"的支持,使用不同的实现:

var cssUserSelect = has("css-user-select");dom.setSelectable = cssUserSelect ? function(node, selectable){// css-user-select returns a (possibly vendor-prefixed) CSS property namedom.byId(node).style[cssUserSelect] = selectable ? "" : "none";} : function(node, selectable){node = dom.byId(node);// (IE < 10 / Opera) Fall back to setting/removing the// unselectable attribute on the element and all its childrenvar nodes = node.getElementsByTagName("*"),i = nodes.length;if(selectable){node.removeAttribute("unselectable");while(i--){nodes[i].removeAttribute("unselectable");}}else{node.setAttribute("unselectable", "on");while(i--){nodes[i].setAttribute("unselectable", "on");}}};

ie中,循环改变所有子节点的unselectable特性来控制选择性。

  分享一条小经验:设置一个元素不可选中时,最好在能满足需求的最远祖先上设置,如果仅仅在一个元素上设置未必能够达到效果;比如:设置一个图片不可选中,但是祖先可以选中,用户可能会祖先选中时会变蓝,看起来好像图片依然能够被选中。

  如果您觉得这篇文章对您有帮助,请不吝点击下方的推荐按钮,您的鼓励是我分享的动力!

dojo/dom源码学习相关推荐

  1. vue实例没有挂载到html上,vue 源码学习 - 实例挂载

    前言 在学习vue源码之前需要先了解源码目录设计(了解各个模块的功能)丶Flow语法. src ├── compiler # 把模板解析成 ast 语法树,ast 语法树优化,代码生成等功能. ├── ...

  2. VUE源码学习第一篇--前言

    一.目的 前端技术的发展,现在以vue,react,angular为代表的MVVM模式以成为主流,这三个框架大有三分天下之势.react和angular有facebook与谷歌背书,而vue是以一己之 ...

  3. Vue源码学习 - 组件化一 createComponent

    Vue源码学习 - 组件化一 createComponent 组件化 createComponent 构造子类构造函数 安装组件钩子函数 实例化 VNode 总结 学习内容和文章内容来自 黄轶老师 黄 ...

  4. php连接tidb,TiDB源码学习笔记:启动TiDB

    作者:院长,神州数码云基地开发工程师,目前专注于TiDB源码研究. TiDB源码研究系列第一篇,简述TiDB的核心架构,从tidb-server/mian.go开始,探索启动TiDB的方法. 最近因为 ...

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

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

  6. Vue源码学习 - 准备工作

    Vue源码学习 - 准备工作 准备工作 认识Flow 为什么用 Flow Flow 的工作方式 类型推断 类型注释 数组 类和对象 null Flow 在 Vue.js 源码中的应用 flow实践 总 ...

  7. Shiro源码学习之二

    接上一篇 Shiro源码学习之一 3.subject.login 进入login public void login(AuthenticationToken token) throws Authent ...

  8. Shiro源码学习之一

    一.最基本的使用 1.Maven依赖 <dependency><groupId>org.apache.shiro</groupId><artifactId&g ...

  9. mutations vuex 调用_Vuex源码学习(六)action和mutation如何被调用的(前置准备篇)...

    前言 Vuex源码系列不知不觉已经到了第六篇.前置的五篇分别如下: 长篇连载:Vuex源码学习(一)功能梳理 长篇连载:Vuex源码学习(二)脉络梳理 作为一个Web前端,你知道Vuex的instal ...

最新文章

  1. grep 命令使用笔记
  2. 给ModalPopupExtender控件添加弹出关闭等事件
  3. docker一次构建,快速部署
  4. python十四:全局变量(global)与局部变量与上一级变量(nonlocal)
  5. 【转】NHibernate入门教程
  6. java异常类层次结构图
  7. 因为没钱买衣服,我女朋友不要我了......
  8. linux文件属性之用户和组基础知识
  9. Python 之内置函数和匿名函数
  10. 声明式事务、Spring 中常用注解、Ajax
  11. android 蓝牙headset,android – 如何通过反射使用BluetoothHeadset类
  12. 小白爬虫第一弹之抓取妹子图【更新版】
  13. 基于tcp的协议需要设计数据校验码吗_C#与宇电温控表自定义协议通信实例
  14. 磁共振立体定向仪行业调研报告 - 市场现状分析与发展前景预测(2021-2027年)
  15. Controller中servletFileUpload.parseRequest(request)解析为空
  16. python用turtle画一个苹果
  17. 下列选项中完整描述计算机操作系统作用是,电影《八部半》中,古依多的童年是他生命的源泉,也是他灵魂的开端。...
  18. 计算机潮流分析22节点,第三章电力系统潮流分析与计算第七讲电力网络方程和矩阵及功率方程_255903070...
  19. H264和aac 封装成为ts,并生成m3u8
  20. 基于Kubernetes构建企业容器云【入门实战篇】- 手动制作CA证书(二)

热门文章

  1. 难道说,这就是专业程序员的办公桌?
  2. PHP IPC函数介绍---共享内存
  3. Ajax应用开发:实践者指南
  4. [记录] 解决img的1px空白问题
  5. BZOJ1566:[NOI2009]管道取珠——题解
  6. 快速生成apk 自动发布到网站 便于测试
  7. 【转】老程序猿给新程序猿的13点建议
  8. [转] boost::function用法详解
  9. JAVA学习笔记--4.多线程编程 part1.背景知识和内存模型
  10. 提高你的Java代码质量吧:让我们疑惑的字符串拼接方式的选择