自定义querySelectorAll实现

时间:2016-02-05 22:58:02

标签: javascript dom selectors-api

这是作为面试问题给我的 - 没有得到这份工作,但我还是想弄清楚。

目标是编写两个querySelectorAll函数:一个名为qsa1,用于由单个标记名称组成的选择符(例如divspan)和另一个名称qsa2,它接受​​任意嵌套的标记选择器(例如p spanol li code)。

我很容易得到第一个,但第二个有点棘手。

我怀疑,为了处理可变数量的选择器,正确的解决方案可能是递归的,但我想我会尝试使用迭代优先工作。这是我到目前为止所得到的:

  qsa2 = function(node, selector) {
    var selectors = selector.split(" ");
    var matches;
    var children;
    var child; 
    var parents = node.getElementsByTagName(selectors[0]);
    if (parents.length > 0) {
        for (var i = 0; i < parents.length; i++) {
            children = parents[i].getElementsByTagName(selectors[1]);
            if (children.length > 0) {
                for (var i = 0; i < parents.length; i++) {
                    child = children[i];
                    matches.push(child); // somehow store our result here
                }
            }
        }
    }
    return matches;
  }

我的代码的第一个问题,除了它不起作用的事实,它只处理两个选择器(但它应该能够清除第一,第二和第四种情况)。

第二个问题是我无法返回正确的结果。我知道,就像在qsa1中一样,我应该返回与我通过调用getElementsByTagName()函数得到的相同结果,其中&#34;返回实时NodeList具有给定标签名称的元素&#34;。创建一个数组并将Node推送或附加到它上面并不是要切割它。

如何撰写正确的回报结果?

(对于上下文,可以找到完整的代码体here

2 个答案:

答案 0 :(得分:0)

以下是我的做法

next

假设我们始终将文档作为上下文开始,然后将选择器拆分为空格,就像您已经在做的那样,并迭代标记名。
在每次迭代中,只需覆盖外部concat变量,然后再次运行循环 我使用了一个数组并matches将结果存储在循环中。

这有点类似于问题中的代码,但应该注意的是,您从不创建数组,实际上undefined变量是pods,并且无法推送到。

答案 1 :(得分:0)

这里有语法错误:

if (parents.length > 0) {
    for (var i = 0; i < parents.length; i++) {
        children = parents[i].getElementsByTagName(selectors[1]);
        if (children.length > 0) { 
            for (var i = 0; i < parents.length; i++) { // <-----------------------

不要超过children的长度,而是超过parent的长度。

以及您正在重复使用迭代变量名称这一事实!这意味着映射到i长度的parent会在child循环中被覆盖!

另外,如果元素为空,for循环不会遍历元素,因此您的检查是多余的。

应该如下:

for (var i = 0; i < parents.length; i++) {
    children = parents[i].getElementsByTagName(selectors[1]);
        for (var k = 0; k < children.length; i++) { 

我建议使用如下的递归解决方案,而不是使用迭代解决方案:

var matches = [];
function recursivelySelectChildren(selectors, nodes){
    if (selectors.length != 0){
        for (var i = 0; i < nodes.length; i++){
            recursivelySelectChildren(nodes[i].getElementsByTagName(selectors[0]), selectors.slice(1))
        }
    } else {
        matches.push(nodes);
    }
}
function qsa(selector, node){
    node = node || document;
    recursivelySelectChildren(selector.split(" "), [node]);
    return matches;
}