dojo/dom源码学习

  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 id
                var 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 form
            if(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 id
                var 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 environments
        if(!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 likely
        do{
            if(typeof style[name] !== "undefined"){
                // Supported; return property name
                return name;
            }
        }while(i-- && (name = prefixes[i] + "UserSelect"));

        // Not supported if we didn‘t return before now
        return 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 name
        dom.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 children
        var 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特性来控制选择性。

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

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

时间: 2024-10-10 10:49:27

dojo/dom源码学习的相关文章

dojo/dom源码

dojo/dom源码学习 dojo/dom模块作为一个基础模块,最常用的就是byId方法.除此之外还有isDescendant和setSelectable方法. dom.byId(myId)方法:   各种前端类库都免不了与DOM节点打交道,操作DOM的方法千变万化最终还是要回到原生的那几个方法中,因为类库再快也快不过原生.所以在dom.byId方法中,还是要依靠document.getElementById('myId')方法.假如没有ie,假如不要考虑兼容性,getElementById方法

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支持.d

jquery源码学习

jQuery 源码学习是对js的能力提升很有帮助的一个方法,废话不说,我们来开始学习啦 我们学习的源码是jquery-2.0.3已经不支持IE6,7,8了,因为可以少学很多hack和兼容的方法. jquery-2.0.3的代码结构如下 首先最外层为一个闭包, 代码执行的最后一句为window.$ = window.jquery = jquery 让闭包中的变量暴露倒全局中. 传参传入window是为了便于压缩 传入undefined是为了undifined被修改,他是window的属性,可以被修

jquery源码学习(一)core部分

这一部分是jquery的核心 jquery的构造器 jquery的核心工具函数 构造器 jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' // Need init if jQuery is called (just allow error to be thrown if not included) return new jQu

jquery源码学习(二)sizzle部分 【转】

一,sizzle的基本原理 sizzle是jquery选择器引擎模块的名称,早在1.3版本就独立出来,并且被许多其他的js库当做默认的选择器引擎.首先,sizzle最大的特点就是快.那么为什么sizzle当时其他引擎都快了,因为当时其他的引擎都是按照从左到右逐个匹配的方式来进行查找的,而sizzle刚好相反是从右到左找的. 举个简单的例子 “.a .b .c”来说明为什么sizzle比较快.这个例子如果按照从左到右的顺序查找,很明显需要三次遍历过程才能结束,即先在document中查找.a,然后

commons-fileupload源码学习心得

commons-fileupload依赖于commons-io包. commons-fileupload的使用方法: 1.创建一个文件项目工厂类DiskFileItemFactory.       DiskFileItemFactory有俩个构造方法: 1 DiskFileItemFactory() 其中sizeThreshold是默认值10kB, 文件大小不超过这个值将内容保存在内存,超过这个值会把文件保存到临时目录下,可用System.getProperty("java.io.tmpdir&

jQuery源码学习笔记:总体架构

1.1.自调用匿名函数: (function( window, undefined ) { // jquery code })(window); 这是一个自调用匿名函数,第一个括号内是一个匿名函数,第二个括号立即执行,传参是window. 1.为什么有自调用匿名函数? 通过定义匿名函数,创建了一个"私有"空间,jQuery必须保证创建的变量不能和导入它的程序发生冲突. 2.为什么传入window? 传入window使得window由全局变量变成局部变量,jQuery访问window时,

jQuery源码学习笔记:构造jQuery对象

3.1源码结构: (function( window, undefined ) { var jQuery = (function() { // 构建jQuery对象 var jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context, rootjQuery ); } // jQuery对象原型 jQuery.fn = jQuery.prototype = { constructor:

jQuery源码学习笔记:扩展工具函数

// 扩展工具函数 jQuery.extend({ // http://www.w3school.com.cn/jquery/core_noconflict.asp // 释放$的 jQuery 控制权 // 许多 JavaScript 库使用 $ 作为函数或变量名,jQuery 也一样. // 在 jQuery 中,$ 仅仅是 jQuery 的别名,因此即使不使用 $ 也能保证所有功能性. // 假如我们需要使用 jQuery 之外的另一 JavaScript 库,我们可以通过调用 $.noC