获取完全相同节点的第n个孩子?

时间:2017-02-01 11:18:30

标签: javascript

我正在构建一个“节点签名”,它将返回如下节点:

div.container>div.row:nth-child(1)>div.list.col-md-6:nth-child(2)

我接近完成它,我只有以下问题:

nth-child值仅考虑节点是否相同(nodeName),但由于该节点上有ID,而某些类,选择器无法正确,因为在这种情况下,有只有一个div#clients,而不是多个。 (即使在同一级别,也有很多div)。

以下是代码:

function getPath(e) {
    var path = [];
    if ('path' in e && e.path.length > 0) {
        for (var i = 0; i < e.path.length; i++) {
            if (e.path[i]) {
                path.push(getNodeSignature(e.path[i]));
            }
            if (e.path[i] === document.body) break;
        }
    } else {
        var node = e.target;
        while(node != document.body) {
            if (node) {
                path.push(getNodeSignature(node));
            }
            node = node.parentNode;
        }
    }

    path.reverse();
    return path.join('>');
}

function getNodeSignature(node) {
    var structure = node.nodeName.toLowerCase();
    if ('id' in node && node.id) {
        structure += '#' + node.id
    }

    var classes = null;
    if ('classList' in node && node.classList.length > 0) {
        classes = Array.prototype.slice.call(node.classList);
    } else if ('className' in node && node.className) {
        var classes = node.className.split(/\s+/);
    }

    if (classes !== null && classes.length > 0) {
        structure += '.' + classes.join('.');
    }

    var position = getIndexInParent(node);
    if (position > 0) {
        structure += ':nth-child(' + position + ')';
    }

    return structure;
}

// The following code doesn't take into consideration the ID and class path
function getIndexInParent(node) {
    var children = node.parentNode.childNodes;
    var num = 0;
    for (var i=0; i<children.length; i++) {
        if (children[i]==node) return num;
        if (children[i].nodeName == node.nodeName) num++;
    }
    return -1;
}

这是一个HTML示例:

<div class="container">
    <div class="row">
        <div class="list col-md-3">Item 1</div>
        <div class="list col-md-3">Item 2</div>
        <div class="list col-md-6">Item 3</div>
        <div class="list col-md-6">Item 4</div>
    </div>
    <hr />
    <div class="row">
        <div class="list col-md-3">Item 1</div>
        <div class="list col-md-3">Item 2</div>
        <div class="list col-md-6">Item 3</div>
        <div class="list col-md-6">Item 4</div>
    </div>
    <hr />
    <div class="row">
        <div class="list col-md-3">Item 1</div>
        <div class="list col-md-3">Item 2</div>
        <div class="list col-md-6">Item 3</div>
        <div class="list col-md-6">Item 4</div>
    </div>
</div>

在这种情况下,如果您点击第二个.row,第三个div,则代码应返回以下内容:

div.container>div.row:nth-child(1)>div.list.col-md-6:nth-child(0)

(n-child应为0,而不是3,因为之前的指示,“div”和“.list.col-md-6”足以减少2项的可能性)

示例https://jsfiddle.net/an2jsq2r/1/

如果您点击第二项,在“第3项”上,您将获得:

body>div.container>div.row:nth-child(1)>div.list.col-md-6:nth-child(2)

哪个错了,因为它应该是:

body>div.container>div.row:nth-child(1)>div.list.col-md-6:nth-child(0)

(注意div.list.col-md-6:nth-child(0))。该代码没有考虑电位ID和类添加的限制来计算元素的位置。

我该怎么办? (注意:通过支持最大范围的浏览器,我需要尽可能广泛。)

2 个答案:

答案 0 :(得分:0)

  

示例:https://jsfiddle.net/an2jsq2r/1/

     

如果您点击第二项,在“第3项”上,您将获得:

     

body>div.container>div.row:nth-child(1)>div.list.col-md-6:nth-child(2)

     

哪个错了,因为它应该是:

     

body>div.container>div.row:nth-child(1)>div.list.col-md-6:nth-child(0)

不,不应该。你误解了:nth-child。它应该是

body>div.container>div.row:nth-child(3)>div.list.col-md-6:nth-child(3)
Note --------------------------------^------------------------------^

...因为第二个div.row是其父级中的第三个元素,该行中的第一个div.list.col-md-6第三个行中的元素。

这是证据:

var path = "body>div.container>div.row:nth-child(3)>div.list.col-md-6:nth-child(3)";
var element = document.querySelector(path);
element.innerHTML = "This one!";
element.style.color = "blue";
<div class="container">
    <div class="row">
        <div class="list col-md-3">Item 1</div>
        <div class="list col-md-3">Item 2</div>
        <div class="list col-md-6">Item 3</div>
        <div class="list col-md-6">Item 4</div>
    </div>
    <hr />
    <div class="row">
        <div class="list col-md-3">Item 1</div>
        <div class="list col-md-3">Item 2</div>
        <div class="list col-md-6">Item 3</div>
        <div class="list col-md-6">Item 4</div>
    </div>
    <hr />
    <div class="row">
        <div class="list col-md-3">Item 1</div>
        <div class="list col-md-3">Item 2</div>
        <div class="list col-md-6">Item 3</div>
        <div class="list col-md-6">Item 4</div>
    </div>
</div>

正如我所说in this comment:nth-child并不关心 关于选择器的其余部分。

更简单的例子:div:nth-child(2)表示“div也是其父亲中的第二个孩子”。 意味着“父母的第二个div孩子。”

要确定要使用的正确:nth-child,您在父级中使用纯粹的基于1的索引,而不考虑完全其他方面,如标记名称,什么它有的课等等。

因此,getIndexInParent应为:

function getIndexInParent(node) {
    var children = node.parentNode.children;    // Note: Not `childNodes`
    for (var i = 0; i < children.length; i++) { // Note: 0 <= n < length
        if (children[i]==node) return i + 1;    // Note: No other criteria, and +1 on value
    }
    return -1;
}

如果我们将其应用于您的小提琴(如果您使用“整页”选项,此代码段效果最佳):

function getPath(e) {
    var path = [];
    if ('path' in e && e.path.length > 0) {
        for (var i = 0; i < e.path.length; i++) {
            if (e.path[i]) {
                path.push(getNodeSignature(e.path[i]));
            }
            if (e.path[i] === document.body) break;
        }
    } else {
        var node = e.target;
        while(node != document.body) {
            if (node) {
                path.push(getNodeSignature(node));
            }
            node = node.parentNode;
        }
    }

    path.reverse();
    return path.join('>');
}

function getNodeSignature(node) {
    var structure = node.nodeName.toLowerCase();
    if ('id' in node && node.id) {
        structure += '#' + node.id
    }

    var classes = null;
    if ('classList' in node && node.classList.length > 0) {
        classes = Array.prototype.slice.call(node.classList);
    } else if ('className' in node && node.className) {
        var classes = node.className.split(/\s+/);
    }

    if (classes !== null && classes.length > 0) {
        structure += '.' + classes.join('.');
    }

    var position = getIndexInParent(node);
    if (position > 0) {
        structure += ':nth-child(' + position + ')';
    }

    return structure;
}

function getIndexInParent(node) {
    var children = node.parentNode.children;    // Note: Not `childNodes`
    for (var i = 0; i < children.length; i++) { // Note: 0 <= n < length
        if (children[i]==node) return i + 1;    // Note: No other criteria, and +1 on value
    }
    return -1;
}

document.addEventListener('click', function (e) {
  var path = getPath(e);
  console.log("path is: " + path);
  var element = document.querySelector(path);
  if (element) {
    element.innerHTML = "You clicked me!";
    element.style.color= "green";
    console.log("updated element");
  } else {
    console.log("That's weird, no element found!");
  }
})
<div class="container">
    <div class="row">
        <div class="list col-md-3">Item 1</div>
        <div class="list col-md-3">Item 2</div>
        <div class="list col-md-6">Item 3</div>
        <div class="list col-md-6">Item 4</div>
    </div>
    <hr />
    <div class="row">
        <div class="list col-md-3">Item 1</div>
        <div class="list col-md-3">Item 2</div>
        <div class="list col-md-6">Item 3</div>
        <div class="list col-md-6">Item 4</div>
    </div>
    <hr />
    <div class="row">
        <div class="list col-md-3">Item 1</div>
        <div class="list col-md-3">Item 2</div>
        <div class="list col-md-6">Item 3</div>
        <div class="list col-md-6">Item 4</div>
    </div>
</div>

或者,如果我们应用您的:nth-of-type提示:

function getTypeIndexInParent(node) {
    var tag = node.tagName;
    var children = node.parentNode.children;
    var child;
    var num = 0;
    for (var i = 0; i < children.length; i++) {
        child = children[i];
        if (child.tagName === tag) {
            ++num;
        }
        if (children[i]==node) return num;
    }
    return -1;
}

我们得到:

function getPath(e) {
    var path = [];
    if ('path' in e && e.path.length > 0) {
        for (var i = 0; i < e.path.length; i++) {
            if (e.path[i]) {
                path.push(getNodeSignature(e.path[i]));
            }
            if (e.path[i] === document.body) break;
        }
    } else {
        var node = e.target;
        while(node != document.body) {
            if (node) {
                path.push(getNodeSignature(node));
            }
            node = node.parentNode;
        }
    }

    path.reverse();
    return path.join('>');
}

function getNodeSignature(node) {
    var structure = node.nodeName.toLowerCase();
    if ('id' in node && node.id) {
        structure += '#' + node.id
    }

    var classes = null;
    if ('classList' in node && node.classList.length > 0) {
        classes = Array.prototype.slice.call(node.classList);
    } else if ('className' in node && node.className) {
        var classes = node.className.split(/\s+/);
    }

    if (classes !== null && classes.length > 0) {
        structure += '.' + classes.join('.');
    }

    var position = getTypeIndexInParent(node);
    if (position > 0) {
        structure += ':nth-of-type(' + position + ')';
    }

    return structure;
}

function getTypeIndexInParent(node) {
    var tag = node.tagName;
    var children = node.parentNode.children;
    var child;
    var num = 0;
    for (var i = 0; i < children.length; i++) {
        child = children[i];
        if (child.tagName === tag) {
            ++num;
        }
        if (children[i]==node) return num;
    }
    return -1;
}

document.addEventListener('click', function (e) {
  var path = getPath(e);
  console.log("path is: " + path);
  var element = document.querySelector(path);
  if (element) {
    element.innerHTML = "You clicked me!";
    element.style.color= "green";
    console.log("updated element");
  } else {
    console.log("That's weird, no element found!");
  }
})
<div class="container">
    <div class="row">
        <div class="list col-md-3">Item 1</div>
        <div class="list col-md-3">Item 2</div>
        <div class="list col-md-6">Item 3</div>
        <div class="list col-md-6">Item 4</div>
    </div>
    <hr />
    <div class="row">
        <div class="list col-md-3">Item 1</div>
        <div class="list col-md-3">Item 2</div>
        <div class="list col-md-6">Item 3</div>
        <div class="list col-md-6">Item 4</div>
    </div>
    <hr />
    <div class="row">
        <div class="list col-md-3">Item 1</div>
        <div class="list col-md-3">Item 2</div>
        <div class="list col-md-6">Item 3</div>
        <div class="list col-md-6">Item 4</div>
    </div>
</div>

答案 1 :(得分:-1)

好的,所以为了仅在必要时使用“:nth-​​of-type”,这意味着只有当其他规则(节点名称+节点id +节点类)对于至少2个孩子来说是相同的时候。< / p>

结果如下:

function getIndexInParent(node) {
    if (node === null || node.parentNode === null) return -1;

    var children = node.parentNode.childNodes,
        freezePosition = false,
        position = 1,  // 1 based index
        quantity = 1; // Already counting the same item
    for (var i = 0; i < children.length; i++) {
        if (node === children[i]) {
            freezePosition = true;
            continue;
        }
        if (node.nodeName === children[i].nodeName) {
            if (!freezePosition) position++;
            quantity++;
        }
    }

    return (quantity > 1 ? position : -1);
}

function getNodeSignature(node, withoutPosition) {
    if (!node || !node.nodeName) return null;

    var structure = node.nodeName.toLowerCase();

    if (withoutPosition !== false) {
        var position = getIndexInParent(node);
        if (position > 0) {
            return structure + ':nth-of-type(' + position + ')';
        }
    }

    if ('id' in node && node.id) {
        structure += '#' + node.id
    }

    var classes = null;
    if ('classList' in node && node.classList.length > 0) {
        classes = Array.prototype.slice.call(node.classList);
    } else if ('className' in node && node.className) {
        var classes = node.className.split(/\s+/);
    }

    if (classes !== null && classes.length > 0) {
        structure += '.' + classes.join('.');
    }

    return structure;
}

function getPath(e) {
    var path = [];
    if ('path' in e && e.path.length > 0) {
        for (var i = 0; i < e.path.length; i++) {
            if (e.path[i] === window.document || e.path[i] === window.document.body) break;
            if (e.path[i]) {
                path.push(getNodeSignature(e.path[i]));
            }
        }
    } else {
        var node = e.target;
        while(true) {
            if (node === window.document || node === window.document.body) break;
            if (node) {
                path.push(getNodeSignature(node));
            }
            node = node.parentNode;
        }
    }

    if (path.length > 0) {
        path.reverse();
        return 'body>' + path.join('>');
    }

    return null;
}

如果有人做得更好,我很想知道如何:)