How do I parse the results of a querySelectorAll selector engine & allow method chaining?

css, css-selectors, html, javascript

Solution

I want to use it like this...:

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

...not like this...:

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

It sounds like you want to have assigning to `innerHTML` on your set of results assign to the `innerHTML` of all of the results.

To do that, you'll have to use a function, either directly or indirectly.

Directly:

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");

There, we define a function, and we assign it to the array you're returning as a property. You can then call that function on the array. (You might want to use `Object.defineProperty` if it's available to set the `setInnerHTML` property, so you can make it non-enumerable.)

Indirectly (requires an ES5-enabled JavaScript engine):

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";

There, we use `Object.defineProperty` to define a setter for the property.

In the comments below you say

I have a few prototypes that work when individually attached to the $ function. Example: `$('div').makeClass('this');` They do not work when they are chained together. Example: `$('div').makeClass('this').takeClass('that');`

To make chaining work, you do `return this;` from each of the functions (so the end of `makeClass` would do `return this;`). That's because when you're chaining, such as `obj.foo().bar()`, you're calling `bar` on the return value of `foo`. So to make chaining work, you make sure `foo` returns `this` (the object on which `foo` was called).

Problem

SIMPLIFIED EXAMPLE CODE: ``` 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); } } } ``` I want to use it like this...: ``` $("div").innerHTML='It works!'; ``` ...not like this...: ``` $("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!'; } ``` This is as close as I got. I would like chaining to work and for it to be compatible with native methods: ``` 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!'); ``` I decided to build this engine to better learn JavaScript; It's working but I am hoping I can learn some more from the kind members of Stack Overflow. Any help would be much appreciated!

Original source