querySelector 和 querySelectorAll 别名

querySelector and querySelectorAll alias

querySelectorquerySelectorAlldocumentElement

创建跨浏览器别名的最佳替代方法是什么

最直接的方法可能是

window.$ = function(selector){return document.querySelector(selector)}
window.$$ = function(selector){return document.querySelectorAll(selector)}

当然这不允许链接,因为函数 return 总是引用 document

$('.parent-class').$$('.child-classes')

到目前为止我最好的选择是

window.$ = document.querySelector.bind(document);
window.$$ = document.querySelectorAll.bind(document);
Element.prototype.$ = Element.prototype.querySelector;
Element.prototype.$$ = Element.prototype.querySelectorAll;

这样我们就可以做之前失败的选择器,虽然我不确定这可能带来的不良影响,有没有人有hint/explanation可以提供?

有谁有好的、不显眼的替代品吗?

最好的不显眼的替代方法是做这样的别名。这是另一回事要记住,对于查看你的代码来学习的人来说是另一回事,它可能会破坏你的 IDE 的自动完成和语法检查功能,并且它会污染原型。

但是,如果您仍然想沿着这条路继续前进,将需要特殊的机器来链接 $$Element.$$ 中的任何内容,因为它们 return 节点列表。首先,我们将 $$ return 设为一个数组:

window.$$ = function(sel) { 
  return Array.prototype.slice.call(document.querySelector(sel)); 
}

Element.$$也是如此。现在我们可以扩充 Array 原型:

Array.prototype.$ = function(sel) {
  var result = [];
  for (var i = 0; i < this.length; i++) {
    var sub = this[i].querySelector(sel);
    if (sub) result.push(sub);
  }
  return result;
};

继续使用 map and/or filter 等重写这个,如果它适合你的喜好。

现在你可以做:

$$('.class1').$('span')

类似地,您可以在 Array 上定义一个版本的 $$,如下所示:

Array.prototype.$$ = function(sel) {
  var result = [];
  for (var i = 0; i < this.length; i++) {
    var elts = this[i].querySelectorAll(sel);
    for (var j = 0; j < elts.length; j++) {
      result.push(elts[j]);
    }
  }
  return result;
;

这将对输入节点列表的每个成员进行 querySelectorAll 调用所产生的所有节点列表合并到一个数组中。

如果您想链接 setAttribute 之类的东西,您需要为 ElementArray 定义版本:

Element.prototype.setAttributeChainable = function() {
  this.setAttribute.apply(this, arguments);
  return this;
}

Array.prototype.setAttributeChainable = function() {
  for (var i = 0; i < this.length; i++) {
    this[i].setAttribute.apply(this[i], arguments);
  }
  return this;
}

要在 $ 什么也没找到时静静地失败,在这种情况下将它安排到 return 一个空数组:

window.$ = function(sel) {
  return document.querySelector(sel) || [];
};

Element.$.

也一样

现在你可以做

$$('.class1') .
  $$('span') .
  setAttributeChainable('style', 'color:blue') .
  $('a') .
  setAttributeChainable('href', 'google.com');

现在继续此练习,使用任何其他您想要链接的 API and/or 适用于节点列表。

如果您对添加到 Array 原型有宗教异议,您将不得不寻找其他方法。

我也使用 bind 方法创建别名。

var $ = document.querySelector.bind(document);
var $$ = document.querySelectorAll.bind(document);

对于第一个元素,我使用了一个函数和 apply,这样它的行为就如同您以 "VanillaJS" 方式调用它一样。

Element.prototype.$ = function() {
  return this.querySelector.apply(this, arguments);
};

Element.prototype.$$ = function() {
  return this.querySelectorAll.apply(this, arguments);
};

这是一个例子:

var $ = document.querySelector.bind(document);
var $$ = document.querySelectorAll.bind(document);

Element.prototype.$ = function() {
  return this.querySelector.apply(this, arguments);
};

Element.prototype.$$ = function() {
  return this.querySelectorAll.apply(this, arguments);
};

alert($('.foo').innerHTML);
alert('There are ' + $$('.foo').length + ' `.foo` nodes');

var parent = $('.parent');
alert(parent.$('.child').innerText);
alert('There are ' + parent.$$('.child').length + ' children');
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>

<body>
  <div class=foo>It works!</div>
  <div class=foo>It worked again!</div>
  <div class=parent>Parent
    <div class=child>Child</div>
    <div class=child>Another Child</div>
  </div>
</body>

</html>

如果您想要链接,则必须创建更精细的东西。您可能想查看 Bliss 支持链接但更像 VanillaJS 的轻量级 (~3kb) 库而不是 jQuery。

unobtrusive alternative

制作一个包装器,这样您就不会扩展 other/native 结构,将其命名为不太可能冲突的东西

var Σ = (function () {
    function Found(nodes) {
        this.nodes = nodes;
    }
    function find(selector) {
        var nodes;
        if (this instanceof Found) {
            nodes = Array.prototype.map.call(this.nodes, e => Array.prototype.slice.call(e.querySelectorAll(selector)));
            nodes = Array.prototype.concat.apply([], nodes);
            return new Found(nodes);
        }
        if (this === window)
            return new Found(Array.prototype.slice.call(document.querySelectorAll(selector)));
        return new Found(Array.prototype.slice.call(this.querySelectorAll(selector)));
    }
    Found.prototype.find = find;
    return find;
}());

所以你可以做类似的事情

Σ('.answer').find('pre').nodes;

要在此处获取答案中的所有 <pre> 节点(即代码位)。您可以根据需要链接 .find


cross browser

jQuery有什么问题?