jQuery 到常规 Javascript 的映射

Posted

技术标签:

【中文标题】jQuery 到常规 Javascript 的映射【英文标题】:jQuery to Conventional Javascript mapping 【发布时间】:2011-10-22 10:30:11 【问题描述】:

我只想将一些 jQuery 方法映射到它们的普通 javascript DOM 方法;

例如

prev()
next()
before()
after()

如果你能提供 jQuery/Javascript 到类似 DOM 操作方法的映射,那就太好了。

【问题讨论】:

为什么不直接看一下jQuery源码? (它的来源是github.com/jquery/jquery/blob/master/src/traversing.js,但它是个脑筋急转弯,它的声明仍然是坚定的“jQuery”,而不是原生DOM方法)。 完整功能中的方法?例如。 prevnext 采用选择器... 【参考方案1】:

这些 jQuery 方法在普通的基于 DOM 的 JS 中都没有精确的一对一模拟。如果他们这样做了,jQuery 就不需要实现自己的方法来完成这些任务。

您可以使用elem.previousSiblingelem.nextSibling 获取元素的上一个和下一个兄弟姐妹。例如,使用这种 html 结构:

<ul>
    <li>First</li>
    <li id="second">Second</li>
</ul>

你会使用这个 JS:

var elem = document.getElementById("second");
var p = elem.previousSibling;
alert(p.nodeType);

在这种情况下,第二个 LI 的前一个兄弟不是第一个 LI。相反,前一个兄弟是两个 LI 标记之间的空白区域。这样做是为了让您可以操作文档中除了实际的 HTML 元素之外的任何文本节点。 nextSibling 属性的工作原理相同。

这在理论上很好,但在实际使用中或多或少会让人头疼,因为您实际上很少或永远不需要操作文档中的空白。要解决此问题,请遍历检查nodeType 的兄弟姐妹。如果 nodeType 为 1,那么它是一个文本节点,所以跳到下一个,直到找到 nodeType 不是 1 的那个。

您可能会发现博客文章 Finding HTML elements using Javascript nextSibling and previousSibling 很有帮助。只要避免他使用的扩展 Object.prototype 的技术——这样做很容易打破 for/in 在对象上的循环。

对于before()after(),DOM 等价物是insertBefore()insertBefore() 在您想要的目标之后运行在兄弟姐妹上。但是,您不能只是将一些 HTML 放入其中并期望它能够工作。相反,您必须手动将每个元素、属性和值创建为 DOM 节点,然后插入它们。例如:

var welcomingDiv;

function sayHi()
  welcomingDiv = document.createElement("div");
  welcomingDiv.innerHTML = "Welcome, and be amazed!";

  
  target = document.getElementById("a-tag-someplace");
  document.body.insertBefore(welcomingDiv, target);

这会将它插入到您的文档中,任何标签的 ID 为“a-tag-someplace”。不过,即使这是一种欺骗,因为 innerHTML 不是官方 JS 标准的一部分。如果您操作正确,则必须创建一个文本节点并将其附加到新创建的 DIV 中。

所以,简而言之:jQuery 让生活变得更加简单。不要在没有充分理由的情况下重新发明***。

【讨论】:

【参考方案2】:
prev() - previousSibling, its a property
next() - nextSibling, its a property
before() - insertBefore,
after() - There is no insertAfter method but we implement it using insertBefore

【讨论】:

我相信它们实际上是 $(node.previousSibling) 等。人。是吗? +1 请注意,“after”可以通过使用“insertBefore”来实现,正如你所提到的,或者在父元素上使用“appendChild”来实现在最后一个兄弟之后添加内容的情况。【参考方案3】:

以下是TreeWalker(play with it at jsbin.com)的JavaScript实现。

<html>
<head></head>
<body>
    <script>
        var NodeFilter = 
            FILTER_ACCEPT: 1,
            FILTER_REJECT: 2,
            FILTER_SKIP: 3,
            SHOW_ALL: -1,
            SHOW_ELEMENT: 1,
            SHOW_ATTRIBUTE: 2,
            SHOW_TEXT: 4,
            SHOW_CDATA_SECTION: 8,
            SHOW_ENTITY_REFERENCE: 16,
            SHOW_ENTITY: 32,
            SHOW_PROCESSING_INSTRUCTIONS: 64,
            SHOW_COMMENT: 128,
            SHOW_DOCUMENT: 256,
            SHOW_DOCUMENT_TYPE: 512,
            SHOW_DOCUMENT_FRAGMENT: 1024,
            SHOW_NOTATION: 2048
        ;

        var TreeWalker = function (root, whatToShow, filter, expandEntityReferences) 
            this.root = root;
            this.whatToShow = whatToShow;
            this.filter = filter;
            this.expandEntityReferences = expandEntityReferences;
            this.currentNode = root;
            this.NodeFilter = NodeFilter;
        ;

        TreeWalker.prototype.parentNode = function () 
            var testNode = this.currentNode;

            do 
                if (
                    testNode !== this.root &&
                    testNode.parentNode &&
                    testNode.parentNode !== this.root
                ) 
                    testNode = testNode.parentNode;
                 else 
                    return null;
                
             while (this._getFilteredStatus(testNode) !== this.NodeFilter.FILTER_ACCEPT);
            (testNode) && (this.currentNode = testNode);

            return testNode;
        ;

        TreeWalker.prototype.firstChild = function () 
            var testNode = this.currentNode.firstChild;

            while(testNode) 
                if(this._getFilteredStatus(testNode) === this.NodeFilter.FILTER_ACCEPT) 
                    break;
                
                testNode = testNode.nextSibling;
            
            (testNode) && (this.currentNode = testNode);

            return testNode;
        ;

        TreeWalker.prototype.lastChild = function () 
            var testNode = this.currentNode.lastChild;

            while (testNode) 
                if(this._getFilteredStatus(testNode) === this.NodeFilter.FILTER_ACCEPT) 
                    break;
                
                testNode = testNode.previousSibling;
            
            (testNode) && (this.currentNode = testNode);

            return testNode;
        ;

        TreeWalker.prototype.nextNode = function () 
            var testNode = this.currentNode;

            while (testNode) 
                if (testNode.childNodes.length !== 0) 
                    testNode = testNode.firstChild;
                 else if (testNode.nextSibling) 
                    testNode = testNode.nextSibling;
                 else 
                    while (testNode) 
                        if (testNode.parentNode && testNode.parentNode !== this.root) 
                            if (testNode.parentNode.nextSibling) 
                                testNode = testNode.parentNode.nextSibling;
                                break;
                             else 
                                testNode = testNode.parentNode;
                            
                        
                        else return null;
                    
                
                if (testNode && this._getFilteredStatus(testNode) === this.NodeFilter.FILTER_ACCEPT) 
                    break;
                
            
            (testNode) && (this.currentNode = testNode);

            return testNode;
        ;

        TreeWalker.prototype.previousNode = function () 
            var testNode = this.currentNode;

            while (testNode) 
                if (testNode.previousSibling) 
                    testNode = testNode.previousSibling;
                    while (testNode.lastChild) 
                        testNode = testNode.lastChild;
                    
                
                else 
                    if (testNode.parentNode && testNode.parentNode !== this.root) 
                        testNode = testNode.parentNode;
                    
                    else testNode = null;
                
                if (testNode && this._getFilteredStatus(testNode) === this.NodeFilter.FILTER_ACCEPT) 
                    break;
                
            
            (testNode) && (this.currentNode = testNode);

            return testNode;
        ;

        TreeWalker.prototype.nextSibling = function () 
            var testNode = this.currentNode;

            while(testNode) 
                (testNode.nextSibling) && (testNode = testNode.nextSibling);
                if(this._getFilteredStatus(testNode) === this.NodeFilter.FILTER_ACCEPT) 
                    break;
                
            
            (testNode) && (this.currentNode = testNode);

            return testNode;
        ;

        TreeWalker.prototype.previousSibling = function () 
            var testNode = this.currentNode;

            while(testNode) 
                (testNode.previousSibling) && (testNode = testNode.previousSibling);
                if(this._getFilteredStatus(testNode) == this.NodeFilter.FILTER_ACCEPT) 
                    break;
                
            
            (testNode) && (this.currentNode = testNode);

            return testNode;
        ;

        TreeWalker.prototype._getFilteredStatus = function (node) 
            var mask = (
                    /* ELEMENT_NODE */ 1: this.NodeFilter.SHOW_ELEMENT,
                    /* ATTRIBUTE_NODE */ 2: this.NodeFilter.SHOW_ATTRIBUTE,
                    /* TEXT_NODE */ 3: this.NodeFilter.SHOW_TEXT,
                    /* CDATA_SECTION_NODE */ 4: this.NodeFilter.SHOW_CDATA_SECTION,
                    /* ENTITY_REFERENCE_NODE */ 5: this.NodeFilter.SHOW_ENTITY_REFERENCE,
                    /* ENTITY_NODE */ 6: this.NodeFilter.SHOW_PROCESSING_INSTRUCTION,
                    /* PROCESSING_INSTRUCTION_NODE */ 7: this.NodeFilter.SHOW_PROCESSING_INSTRUCTION,
                    /* COMMENT_NODE */ 8: this.NodeFilter.SHOW_COMMENT,
                    /* DOCUMENT_NODE */ 9: this.NodeFilter.SHOW_DOCUMENT,
                    /* DOCUMENT_TYPE_NODE */ 10: this.NodeFilter.SHOW_DOCUMENT_TYPE,
                    /* DOCUMENT_FRAGMENT_NODE */ 11: this.NodeFilter.SHOW_DOCUMENT_FRAGMENT,
                    /* NOTATION_NODE */ 12: this.NodeFilter.SHOW_NOTATION
                )[node.nodeType];

            return (
                (mask && (this.whatToShow & mask) == 0) ?
                    this.NodeFilter.FILTER_REJECT :
                    (this.filter && this.filter.acceptNode) ?
                        this.filter.acceptNode(node) :
                        this.NodeFilter.FILTER_ACCEPT
            );
        ;

        if (!document.createTreeWalker) 
            document.createTreeWalker = function (root, whatToShow, filter, expandEntityReferences) 
                return new TreeWalker(root, whatToShow, filter, expandEntityReferences);
            ;
        

        window.onload = function () 
            var walker = document.createTreeWalker(document.getElementById('rootNodeDiv'), NodeFilter.SHOW_ELEMENT, null, false);

            alert('currentNode: ' + walker.currentNode.id + ': firstChild: '      + walker.firstChild().id);
            alert('currentNode: ' + walker.currentNode.id + ': nextNode: '        + walker.nextNode().id);
            alert('currentNode: ' + walker.currentNode.id + ': lastChild: '       + walker.lastChild().id);
            alert('currentNode: ' + walker.currentNode.id + ': parentNode: '      + walker.parentNode().id);
            alert('currentNode: ' + walker.currentNode.id + ': previousNode: '    + walker.previousNode().id);
            alert('currentNode: ' + walker.currentNode.id + ': nextSibling: '     + walker.nextSibling().id);
            alert('currentNode: ' + walker.currentNode.id + ': previousSibling: ' + walker.previousSibling().id);
        ;
    </script>
    <div id="parentNodeDiv">
        <div id="rootNodeDiv">
            <span id="previousNodeDiv"></span>
            <span id="span01">
                <p id="span01p01">&nbsp;</p>
                <p id="span01p02">&nbsp;</p>
            </span>
            <span id="span02"></span>
            <span id="span03">
                <p id="span02p01">&nbsp;</p>
            </span>
            <span id="span04"></span>
            <span id="span05"></span>
        </div>
    </div>
</body>

【讨论】:

以上是关于jQuery 到常规 Javascript 的映射的主要内容,如果未能解决你的问题,请参考以下文章

如何将 jQuery each() 变成常规的 javascript 循环

从表单到 javaScript 映射 [重复]

使用 jquery 将 XML 字符串映射到 HTML 页面

如何使用常规 JavaScript 实现前置和附加?

如何实现prepend并使用常规JavaScript附加?

一个jQuery插件,它将常规的选择框变成类似Quicksilver的灵活匹配、增量查找控件。