7

DOM4 compareDocumentPosition

私はcompareDocumentPositionを実装したい. Resig はまさにこれを行う上で素晴らしいスタートを切りました。私は彼のコードを取得し、それを整理しました

function compareDocumentPosition(other) {
    var ret = 0;
    if (this.contains) {
        if (this !== other && this.contains(other)) {
            ret += 16;
        }
        if (this !== other && other.contains(this)) {
            ret += 8;
        }
        if (this.sourceIndex >= 0 && other.sourceIndex >= 0) {
            if (this.sourceIndex < other.sourceIndex) {
                ret += 4;
            }
            if (this.sourceIndex > other.sourceIndex) {
                ret += 2;
            }
        } else {
            ret += 1;
        }
    } 
    return ret;
}

これは には機能しますが、またはElementには機能しません。これは、IE8 がこれらのノードで提供しないためです。(それはどちらも与えませんが、私はすでにその問題を修正しました)TextDocumentFragment.sourceIndex.contains

DOCUMENT_POSITION_FOLLOWINGおよびDOCUMENT_POSITION_PRECEDING+=4に対応するand+=2ビットを効率的に記述するにはどうすればよいですか。

追加の参照として、これら 2 つは DOM4 が定義するツリー順序によって定義されます。

オブジェクト A と B が同じツリーにあり、ツリーの順序で A が B より前にある場合、オブジェクト A はオブジェクト B よりも前になります。

オブジェクト A と B が同じツリーにあり、ツリー順で A が B の後にある場合、オブジェクト A はオブジェクト B をフォローしています。

ツリーの順序は事前順、深さ優先のトラバーサルです。

最新のブラウザーのほとんどはこれを実装しています (IE9 を含む)。したがって、IE8 で動作するものだけが必要です (IE6/7 は気にしませんが、うまく動作する場合は!)

4

2 に答える 2

10
function recursivelyWalk(nodes, cb) {
    for (var i = 0, len = nodes.length; i < len; i++) {
        var node = nodes[i];
        var ret = cb(node);
        if (ret) {
            return ret;
        }
        if (node.childNodes && node.childNodes.length) {
            var ret = recursivelyWalk(node.childNodes, cb);
            if (ret) {
                return ret;
            }
        }
    }
}

function testNodeForComparePosition(node, other) {
    if (node === other) {
        return true;
    }
}

function compareDocumentPosition(other) {
    function identifyWhichIsFirst(node) {
        if (node === other) {
            return "other";
        } else if (node === reference) {
            return "reference";
        }
    }

    var reference = this,
        referenceTop = this,
        otherTop = other;

    if (this === other) {
        return 0;
    }
    while (referenceTop.parentNode) {
        referenceTop = referenceTop.parentNode;
    }
    while (otherTop.parentNode) {
        otherTop = otherTop.parentNode;
    }

    if (referenceTop !== otherTop) {
        return Node.DOCUMENT_POSITION_DISCONNECTED;
    }

    var children = reference.childNodes;
    var ret = recursivelyWalk(
        children,
        testNodeForComparePosition.bind(null, other)
    );
    if (ret) {
        return Node.DOCUMENT_POSITION_CONTAINED_BY +
            Node.DOCUMENT_POSITION_FOLLOWING;
    }

    var children = other.childNodes;
    var ret = recursivelyWalk(
        children, 
        testNodeForComparePosition.bind(null, reference)
    );
    if (ret) {
        return Node.DOCUMENT_POSITION_CONTAINS +
            Node.DOCUMENT_POSITION_PRECEDING;
    }

    var ret = recursivelyWalk(
        [referenceTop],
        identifyWhichIsFirst
    );
    if (ret === "other") {
        return Node.DOCUMENT_POSITION_PRECEDING;
    } else {
        return Node.DOCUMENT_POSITION_FOLLOWING;
    }
}

私はそれを自分で書きました。この実装にはバグがあると思いましたが、それは私の他のコードのバグでした。かなりしっかりしているようです。

于 2011-11-30T23:51:57.833 に答える
0

Raynos からの回答は最高のスタートですが、そのままでは実行できません。Node.*が見つから.bindず、IE8 では使用できません。

Internet Explorer 8 ですぐに使用できるコードを次に示します。

function recursivelyWalk(nodes, cb) {
    for (var i = 0, len = nodes.length; i < len; i++) {
        var node = nodes[i];
        var ret = cb(node);
        if (ret) {
            return ret;
        }
        if (node.childNodes && node.childNodes.length) {
            var ret = recursivelyWalk(node.childNodes, cb);
            if (ret) {
                return ret;
            }
        }
    }
}

function testNodeForComparePosition(node, other) {
    if (node === other) {
        return true;
    }
}

var DOCUMENT_POSITION_DISCONNECTED = 1;
var DOCUMENT_POSITION_PRECEDING = 2;
var DOCUMENT_POSITION_FOLLOWING = 4;
var DOCUMENT_POSITION_CONTAINS = 8;
var DOCUMENT_POSITION_CONTAINED_BY = 16;

function compareDocumentPosition(thisNode, other) {
    function identifyWhichIsFirst(node) {
        if (node === other) {
            return "other";
        } else if (node === reference) {
            return "reference";
        }
    }

    var reference = thisNode,
        referenceTop = thisNode,
        otherTop = other;

    if (this === other) {
        return 0;
    }
    while (referenceTop.parentNode) {
        referenceTop = referenceTop.parentNode;
    }
    while (otherTop.parentNode) {
        otherTop = otherTop.parentNode;
    }

    if (referenceTop !== otherTop) {
        return DOCUMENT_POSITION_DISCONNECTED;
    }

    var children = reference.childNodes;
    var ret = recursivelyWalk(
        children,
        function(p) {
            (function() {
                var localOther = other;
                return testNodeForComparePosition(localOther, p);
            })();
        }
    );
    if (ret) {
        return DOCUMENT_POSITION_CONTAINED_BY +
            DOCUMENT_POSITION_FOLLOWING;
    }

    var children = other.childNodes;
    var ret = recursivelyWalk(
        children,
        function(p) {
            (function() {
                var localOther = reference;
                return testNodeForComparePosition(localOther, p);
            })();
        }
    );
    if (ret) {
        return DOCUMENT_POSITION_CONTAINS +
            DOCUMENT_POSITION_PRECEDING;
    }

    var ret = recursivelyWalk(
        [referenceTop],
        identifyWhichIsFirst
    );
    if (ret === "other") {
        return DOCUMENT_POSITION_PRECEDING;
    } else {
        return DOCUMENT_POSITION_FOLLOWING;
    }
}

次のように呼び出します。

compareDocumentPosition(sourceElement, elementToTest)

(それは電話のようなものですsourceElement.compareDocumentPosition(elementToTest))

于 2014-03-04T14:28:52.080 に答える