zoukankan      html  css  js  c++  java
  • 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特性来控制选择性。

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

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

  • 相关阅读:
    Python基础闯关失败总结
    fileinput
    squid安装配置
    服务器添加ipa MIME 类型,防止ipa下载后变zip后缀
    单例之懒汉式和饿汉式
    java集合类,详解
    Android 常用动画
    activity和fragment之前运行的生命周期
    Handler 消息传递机制
    startActivityForResult的用法,以及intent传递图片
  • 原文地址:https://www.cnblogs.com/dojo-lzz/p/4811941.html
Copyright © 2011-2022 走看看