如何解析querySelectorAll选择器引擎的结果并允许方法链接?

3

简化的代码示例:

var $ = function(selector, node) { // Selector engine
    var selector = selector.trim(), node = node || document.body;
    if (selector != null) {
        return Array.prototype.slice.call(node.querySelectorAll(selector), 0); }
    }
}

我希望能像这样使用它...:

$("div").innerHTML='It works!';

不要像这样:

$("div")[0].innerHTML='It works only on the specified index!';

...or this:

for(i=0;i<$('div');i++) {
        $("div")[i].innerHTML='It works great but it's ugly!';
}

这是我能做到的最接近的翻译。我希望链接起来并且能够兼容原生方法:
if(!Array.prototype.innerHTML) { 
    Array.prototype.innerHTML = function(html) {
        for (var i = 0; i < this.length; i++) {
            this[i].innerHTML = html;
        }
    }
}

$("div").innerHTML('It works, but it ruins method chaining!');

我决定构建这个引擎来更好地学习JavaScript;它正在工作,但我希望能从Stack Overflow的友好成员那里学到更多。非常感谢任何帮助!


我会查看一些相关的库和框架,如jQuery、MooTools、Prototype、YUI等,来审查它们是如何实现.html().text()源代码的。 - Jared Farrish
2个回答

5

I want to use it like this...:

$("div").innerHTML='It works!';

...not like this...:

$("div")[0].innerHTML='It works only on the specified index!';

看起来您想将结果集的赋值分配给所有结果的 innerHTML

为了实现这一点,您需要使用一个函数,可以直接或间接地使用。

直接使用:

var $ = function(selector, node) { // Selector engine
    var selector = selector.trim(),
        node = node || document.body,
        rv;
    if (selector != null) {
        rv = Array.prototype.slice.call(node.querySelectorAll(selector), 0); }
        rv.setInnerHTML = setInnerHTML;
    }
    return rv;
}
function setInnerHTML(html) {
    var index;

    for (index = 0; index < this.length; ++index) {
        this[index].innerHTML = html;
    }
}

// Usage
$("div").setInnerHTML("The new HTML");

在这里,我们定义了一个函数,并将其分配给作为属性返回的数组。然后您可以在该数组上调用该函数。(如果可用,您可能需要使用Object.defineProperty来设置setInnerHTML属性,以便使其不可枚举。)

间接地(需要ES5启用的JavaScript引擎):

var $ = function(selector, node) { // Selector engine
    var selector = selector.trim(),
        node = node || document.body,
        rv;
    if (selector != null) {
        rv = Array.prototype.slice.call(node.querySelectorAll(selector), 0); }
        Object.defineProperty(rv, "innerHTML", {
            set: setInnerHTML
        });
    }
    return rv;
}
function setInnerHTML(html) {
    var index;

    for (index = 0; index < this.length; ++index) {
        this[index].innerHTML = html;
    }
}

// Usage
$("div").innerHTML = "The new HTML";

在这里,我们使用Object.defineProperty来为属性定义一个setter。

在下面的评论中,您说:

我有一些原型可以在单独附加到$函数时工作。例如:$('div').makeClass('this');当它们链接在一起时,它们不起作用。例如:$('div').makeClass('this').takeClass('that');

为了使链接起作用,您需要从每个函数中返回this;(因此makeClass的结尾将执行return this;)。这是因为当您链接对象方法时,例如obj.foo().bar(),您正在调用foo的返回值上的bar。因此,为了使链接起作用,您确保foo返回this(即调用foo的对象)。


谢谢您的快速回复。我只是为了演示而使用innerHTML。我真的很想知道是否可以使用其他方法来使用引擎。 - abbotto
1
@o0110o:你只是使用选择器引擎获取匹配列表,如果你想在结果对象上有更多的方法,你有两个选择:要么在创建每个实例时将它们分配给它们,要么(这可能是更好的选择)使你的结果对象不是真正的数组,而是使用原型创建的具有你想要添加的附加功能的东西。 - T.J. Crowder
@o0110o - 这里是使用你演示的 Array 方法的简单描述:http://jsfiddle.net/w52QH/ 在你最后一个评论(在我的评论之后),你需要从你链接的方法中返回 object,以便它可以访问原始对象。 - Jared Farrish
1
为了使链式调用生效,您需要在每个函数中使用 return this(因此,在 makeClass 的末尾应该使用 return this)。 - T.J. Crowder
谢谢大家的建议。我会尝试一下,然后在这里发布结果。干杯! - abbotto
显示剩余2条评论

0

这是可行的;它的语法与我之前示例中给出的略有不同,但最终结果是相同的。我得到了其他 Stack Exchange 成员的大力帮助,再次感谢大家。

var $ = function(selector, node) { // Selector engine
    var selector = selector.trim(), node = node || document.body;
    if (selector != null) {
        return Array.prototype.slice.call(node.querySelectorAll(selector), 0); }
    }
}

if(!Array.prototype.html) { 
    Array.prototype.html = function(html) {
        for (var i = 0; i < this.length; i++) {
            this[i].innerHTML = html;
        }
        return this; //<---- Silly me, my original code was missing this.
    }
}

当我运行它时,所有的东西(包括链接)都按预期工作:

$("div").html('hello world');

输出:

<div>hello world</div>

干杯!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接