小松的技术博客

六和敬

若今生迷局深陷,射影含沙。便许你来世袖手天下,一幕繁华。 你可愿转身落座,掌间朱砂,共我温酒煮茶。

DOM之Range操作

Range是DOM标准提供的来用于在某个范围内操作DOM节点的对象,可以通过document.createRange()Selection对象的getRangeAt()方法获得。

兼容性

IE9及其以下版本不支持Range对象。我们可以通过以下方式检测:

if (document.implementation && document.implementation.hasFeature 
  && document.implementation.hasFeature("Range", "2.0")) {
    // 支持
    var oRange = document.createRange();    
} else {
    // 不支持
}

相关属性:

  • startContainer:包含“起点”的节点。
  • endContainer:包含“结束点”的节点。
  • startOffset:“起点”在startContainer中的偏移量:
    • 如果startContainer是文本节点、注释节点或CDATA节点,则返回“起点”在startContainer中字符偏移量。
    • 如果startContainer是元素节点,则返回“起点”在startContainer.childNodes中的次序。
  • endOffset:“起点”在endContainer中的偏移量,其细节同startOffset
  • commonAncestorContainer:第一个包含Range的节点,同时包含起点和结束点。
  • collapsed:起点和结束点在一起时为true;Range对象为空(刚createRange()时)也为true。

设置起点的方法:

setStart(node, offset)setEnd(node, offset)

  • setStart:设置起点的位置,node是对startContainer的引用,偏移则是startOffset
  • setEnd:设置结束点的位置,node是对endContainer的引用,偏移则是startOffset

setStartBefore(referenceNode)setStartAfter(referenceNode)setEndBefore(referenceNode)setEndAfter(referenceNode)

  • setStartBefore:将“起点”设置到referenceNode前
  • setStartAfter:将“起点”设置到referenceNode后
  • setEndBefore:将“结束点”设置到referenceNode前
  • setEndAfter:将“结束点”设置到referenceNode后

注意:使用这四个方法设置的“起点”或“结束点”的父节点与referenceNode的父节点是同一个元素。

selectNode(referenceNode)selectNodeContents(referenceNode)

  • selectNode:设置Range的范围,包括referenceNode和它的所有后代(子孙)节点。
  • selectNodeContents:设置Range的范围,包括它的所有后代节点(不包括referenceNode)。

collapse(toStart)

折叠该范围,使它的“起点”和“结束点”重合。

参数toStart为true时折叠到Range边界的首部,为false时折叠到Range尾部,默认为false。

修改范围

  • cloneContents():可以克隆选中Range的fragment并返回改fragment。这个方法类似于extractContents(),但是不是删除,而是克隆。
  • deleteContents():从Dom中删除Range选中的fragment。注意该函数没有返回值(实际上为undefined)。
  • extractContents():将选中的Range从DOM树中移到一个fragment中,并返回此fragment。
  • insertNode(refNode):方法可以插入一个节点到Range中,注意会插入到Range的“起点”。
  • surroundContents(refNode):将range包裹的HTML用refNode包裹起来,注意如果range包含的HTML不是闭合的话会报错。

范围比较:compareBoundaryPoints()

var compare = comparerange.compareBoundaryPoints(how, sourceRange);
  • compare:返回1, 0, -1.(0为相等,1为时,comparerange在sourceRange之后,-1为comparerange在sourceRange之前)。
  • how:比较哪些边界点,为常数。
    • Range.START_TO_START:比较两个 Range 节点的开始点
    • Range.END_TO_END:比较两个 Range 节点的结束点
    • Range.START_TO_END:用 sourceRange 的开始点与当前范围的结束点比较
    • Range.END_TO_START:用 sourceRange 的结束点与当前范围的开始点比较
  • sourceRange:要对比的那个Range对象的边界。

节点克隆:cloneRange()

var oNewRange = oRange.cloneRange();

释放Rnage

oRange.detach();

当你创建了Range对象最好用detach()方法来清除它所占的系统资源。虽然不清除,GC(垃圾收集器)也会将其收集,但用detach()释放是一个好习惯.

createContextualFragment

createContextualFragment原本是firfox上推出的私有实现,不过目前IE10及其以上和标准浏览器都支持这种方法了。它允许我们把字符串转换为文档碎片,然后由你决定插入到哪里。我们可以用它来模拟IE下的insertAdjacentHTML()

if(typeof HTMLElement !== "undefined" && !HTMLElement.prototype.insertAdjacentHTML){
    HTMLElement.prototype.insertAdjacentHTML = function (sWhere, sHTML) {
        var df; 
        var r = this.ownerDocument.createRange();
        switch (String(sWhere).toLowerCase()) {
            case "beforebegin":
                r.setStartBefore(this);
                df = r.createContextualFragment(sHTML);
                this.parentNode.insertBefore(df, this);
                break;
            case "afterbegin":
                r.selectNodeContents(this);
                r.collapse(true);
                df = r.createContextualFragment(sHTML);
                this.insertBefore(df, this.firstChild);
                break;
            case "beforeend":
                r.selectNodeContents(this);
                r.collapse(false);
                df = r.createContextualFragment(sHTML);
                this.appendChild(df);
                break;
            case "afterend":
                 r.setStartAfter(this);
                 df = r.createContextualFragment(sHTML);
                 this.parentNode.insertBefore(df, this.nextSibling);
                 break;
        }
    };
}

参考:
http://www.never-online.net/blog/article.asp?id=115
http://www.cnblogs.com/rainman/archive/2011/02/28/1967488.html

←支付宝← →微信 →
comments powered by Disqus