直接子代的 d3 选择器

问题描述 投票:0回答:9

我显然可以做到这一点:

d3.selectAll('div#some-div>ul')

但是如果我使用 DOM 节点或现有的 D3 选择怎么办:

d3.select(this).selectAll('ul')

将为我提供所有后代 UL。所以,如果

var div = d3.select('div') 

给我这个节点:

<div>
  <ul>
    <li>foo
      <ul><li>bar</li></ul>
    </li>
  <ul>
</div>

然后

var uls = div.selectAll('ul') 

会给我两个 UL。我想我可以区分出顶级的:

uls.filter(function() { return this.parentNode === div.node() }

所以,我已经回答了我自己的问题。也许这对某人有用。或者也许有人可以推荐一个不那么丑陋的解决方案。

更好的是,Alain Dumesny,他的下面的答案后来被选为正确的,将其作为问题发布到 D3 并从源头解决了问题,没有拼凑!(为了方便起见,我将其复制到此处,但是那么人们可能不会向下滚动并为他的英雄壮举投下应有的赞成票。)

d3.js
9个回答
28
投票

我没想到这会起作用,但看起来 D3 会子选择作为选择的子元素的任何元素 并且 与选择器匹配 - 所以这有效:

d3.select(this).selectAll('div > ul');

参见http://jsfiddle.net/g3aay/2/


25
投票

如果有人仍然感兴趣,

d3.select(this.childNodes)
正在帮助我解决挑选所有直系孩子的问题。或者,您可以使用

selection.select(function(){
  return this.childNodes;
})

6
投票

d3 Selection v2.0 现在应该使用新的

selection.selectChildren()
/
selection.selectChild()
方法内置此功能 - 请参阅 https://github.com/d3/d3-selection/issues/243


5
投票

@nrabinowitz 的解决方案并不总是有效。

就我而言,我正在尝试做

d3.select(this).selectAll(".childNode > *")

所以我试图获取 .childNode 的所有直接子节点。问题是这是一个嵌套堆栈,因此 .childNode 也可能出现在子级中,这会导致问题。

我发现的最好方法是:

var child = d3.select(this).select(".childNode");
var sel = d3.select(this).selectAll(".childNode > *").filter(function() { 
    return this.parentNode == child.node();
});

3
投票

selectAll
方法依赖于
querySelectorAll
本机方法(至少在v4中)。

这意味着您可以使用

:scope
伪选择器 :

var uls = div.selectAll(':scope > ul') 

:scope
伪选择器目前是草案规范,尚未在所有浏览器中受支持。有关
:scope
伪选择器的更多信息,请访问 MDN


0
投票

基于 Sigfrid 的解决方案,以下是我在我从事的项目中添加到原型中的内容。

  /**
   * Helper that allows to select direct children.
   * See https://stackoverflow.com/questions/20569670/d3-selector-for-immediate-children
   *
   * @param {string} selector
   * @returns {Selection}
   */
  d3.selectAll('__nonexisting__').__proto__.MYPREFIX_selectChildren = function (selector) {
    var expectedParent = this.node();
    return this.selectAll(selector).filter(
      function() {
        return this.parentNode === expectedParent;
      }
    );
  };

我获取原型对象的方式看起来有点笨拙。也许有更好的方法。

“MYPREFIX_”旨在防止名称冲突。

jsdoc

@returns {Selection}
不明确,不幸的是,这种类型是在闭包中声明的,并且没有 jsdoc 可引用的全局名称(据我所知)。

包含此文件后,您可以执行以下操作:

d3.select('#some_id').MYPREFIX_selectChildren('ul')

0
投票

看起来 d3 曾经构建了一些函数来解决这个确切的问题 - 但由于某种原因它们被删除了。 通过将此代码粘贴到您的程序中,您可以再次将它们添加回来:

function childMatcher(selector) {
  return function(node) {
    return node.matches(selector);
  };
}

function children() {
  return this.children;
}

function childrenFilter(match) {
  return function() {
    return Array.prototype.filter.call(this.children, match);
  };
}

/**
 * Runs the css selector only on the immediate children.
 * See: https://stackoverflow.com/questions/20569670/d3-selector-for-immediate-children
 * Use: https://github.com/d3/d3-selection/commit/04e9e758c80161ed6b7b951081a5d5785229a8e6
 *
 * Example Input: selectChildren("form")
 */
d3.selection.prototype.selectChildren = function(match) {
  return this.selectAll(match == null ? children
      : childrenFilter(typeof match === "function" ? match : childMatcher(match)));
}

function childFind(match) {
  return function() {
    return Array.prototype.find.call(this.children, match);
  };
}

function childFirst() {
  return this.firstElementChild;
}

/**
 * Runs the css selector only on the immediate children and returns only the first match.
 * See: https://stackoverflow.com/questions/20569670/d3-selector-for-immediate-children
 * Use: https://github.com/d3/d3-selection/commit/04e9e758c80161ed6b7b951081a5d5785229a8e6
 *
 * Example Input: selectChild("form")
 */
d3.selection.prototype.selectChild = function(match) {
  return this.select(match == null ? childFirst
      : childFind(typeof match === "function" ? match : childMatcher(match)));
}

如果您使用的是打字稿,那么这里是您可以包含在同一文件中的函数声明:

declare module "d3" {
    interface Selection<GElement extends d3.BaseType, Datum, PElement extends d3.BaseType, PDatum> {
        selectChild(match: string | null | Function): Selection<GElement, Datum, PElement, PDatum>;
        selectChildren(match: string | null | Function): Selection<GElement, Datum, PElement, PDatum>;
    }
}

这是实现此功能的小提琴:https://jsfiddle.net/Kade333/drw3k49j/12/


0
投票

使用OP的例子

d3.select('ul')

完成工作。从文档它说:

d3.select
选择与指定选择器字符串匹配的第一个元素...如果多个元素与选择器匹配,则仅选择第一个匹配元素(按文档顺序)


-1
投票

无论四年后它的价值如何,都可以使用

​d3.selectAll('#id > *')
,例如在
​d3.selectAll('#id > *').remove()
中删除 id=
id

的元素的所有子元素
© www.soinside.com 2019 - 2024. All rights reserved.