Skip to content

Commit

Permalink
Add selection.selectChild[ren].
Browse files Browse the repository at this point in the history
  • Loading branch information
mbostock committed Aug 12, 2020
1 parent 248aeba commit 78c9409
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 0 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,14 @@ This method is not intended for concatenating arbitrary selections, however: if
Returns the selection (for symmetry with [<i>transition</i>.selection](https://github.com/d3/d3-transition/blob/master/README.md#transition_selection)).
<a name="selection_selectChild" href="#selection_selectChild">#</a> <i>selection</i>.<b>selectChild</b>([<i>selector</i>]) · [Source](https://github.com/d3/d3-selection/blob/master/src/selection/selectChild.js)
Returns a new selection with the (first) child of each element of the current selection matching the *selector*. If no *selector* is specified, selects the first child (if any). If the *selector* is specified as a string, selects the first child that matches (if any). If the *selector* is a function, it is evaluated for each of the children nodes, in order, being passed the child (*child*), the child’s index (*i*), and the list of children (*children*); the method selects the first child for which the selector return truthy, if any.
<a name="selection_selectChildren" href="#selection_selectChildren">#</a> <i>selection</i>.<b>selectChildren</b>([<i>selector</i>]) · [Source](https://github.com/d3/d3-selection/blob/master/src/selection/selectChildren.js)
Returns a new selection with the children of each element of the current selection matching the *selector*. If no *selector* is specified, selects all the children. If the *selector* is specified as a string, selects the children that match (if any). If the *selector* is a function, it is evaluated for each of the children nodes, in order, being passed the child (*child*), the child’s index (*i*), and the list of children (*children*); the method selects all children for which the selector return truthy.
<a name="matcher" href="#matcher">#</a> d3.<b>matcher</b>(<i>selector</i>) · [Source](https://github.com/d3/d3-selection/blob/master/src/matcher.js)
Given the specified *selector*, returns a function which returns true if `this` element [matches](https://developer.mozilla.org/en-US/docs/Web/API/Element/matches) the specified selector. This method is used internally by [*selection*.filter](#selection_filter). For example, this:
Expand Down
7 changes: 7 additions & 0 deletions src/matcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,10 @@ export default function(selector) {
return this.matches(selector);
};
}

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

4 changes: 4 additions & 0 deletions src/selection/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import selection_select from "./select.js";
import selection_selectAll from "./selectAll.js";
import selection_selectChild from "./selectChild.js";
import selection_selectChildren from "./selectChildren.js";
import selection_filter from "./filter.js";
import selection_data from "./data.js";
import selection_enter from "./enter.js";
Expand Down Expand Up @@ -50,6 +52,8 @@ Selection.prototype = selection.prototype = {
constructor: Selection,
select: selection_select,
selectAll: selection_selectAll,
selectChild: selection_selectChild,
selectChildren: selection_selectChildren,
filter: selection_filter,
data: selection_data,
enter: selection_enter,
Expand Down
18 changes: 18 additions & 0 deletions src/selection/selectChild.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {childMatcher} from "../matcher.js";

var find = Array.prototype.find;

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

function childFirst() {
return this.firstElementChild;
}

export default function(match) {
return this.select(match == null ? childFirst
: childFind(typeof match === "function" ? match : childMatcher(match)));
}
18 changes: 18 additions & 0 deletions src/selection/selectChildren.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {childMatcher} from "../matcher.js";

var filter = Array.prototype.filter;

function children() {
return this.children;
}

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

export default function(match) {
return this.selectAll(match == null ? children
: childrenFilter(typeof match === "function" ? match : childMatcher(match)));
}
65 changes: 65 additions & 0 deletions test/selection/selectChildren-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
var tape = require("tape"),
jsdom = require("../jsdom"),
d3 = require("../../");

tape("select.selectChild(…) selects the first (matching) child", function(test) {
var document = jsdom("<h1><span>hello</span>, <span>world<span>!</span></span></h1>");
var sel = d3.select(document).select("h1");
test.ok(sel.selectChild(() => true) instanceof d3.selection);
test.deepEqual(sel.selectChild(() => true), sel.select("*"));
test.ok(sel.selectChild() instanceof d3.selection);
test.ok(sel.selectChild("*") instanceof d3.selection);
test.deepEqual(sel.selectChild("*"), sel.select("*"));
test.deepEqual(sel.selectChild(), sel.select("*"));
test.deepEqual(sel.selectChild("div"), sel.select("div"));
test.equal(sel.selectChild("span").text(), "hello");
test.end();
});

tape("selectAll.selectChild(…) selects the first (matching) child", function(test) {
var document = jsdom(`
<div><span>hello</span>, <span>world<span>!</span></span></div>
<div><span>hello2</span>, <span>world2<span>!2</span></span></div>
`);
var sel = d3.select(document).selectAll("div");
test.ok(sel.selectChild(() => true) instanceof d3.selection);
test.deepEqual(sel.selectChild(() => true), sel.select("*"));
test.ok(sel.selectChild() instanceof d3.selection);
test.ok(sel.selectChild("*") instanceof d3.selection);
test.deepEqual(sel.selectChild("*"), sel.select("*"));
test.deepEqual(sel.selectChild(), sel.select("*"));
test.deepEqual(sel.selectChild("div"), sel.select("div"));
test.equal(sel.selectChild("span").text(), "hello");
test.end();
});


tape("select.selectChildren(…) selects the matching children", function(test) {
var document = jsdom("<h1><span>hello</span>, <span>world<span>!</span></span></h1>");
var sel = d3.select(document).select("h1");
test.ok(sel.selectChildren("*") instanceof d3.selection);
test.equal(sel.selectChildren("*").text(), "hello");
test.equal(sel.selectChildren().size(), 2);
test.equal(sel.selectChildren("*").size(), 2);
test.deepEqual(sel.selectChildren(), sel.selectChildren("*"));
test.equal(sel.selectChildren("span").size(), 2);
test.equal(sel.selectChildren("div").size(), 0);
test.end();
});

tape("selectAll.selectChildren(…) selects the matching children", function(test) {
var document = jsdom(`
<div><span>hello</span>, <span>world<span>!</span></span></div>
<div><span>hello2</span>, <span>world2<span>!2</span></span></div>
`);
var sel = d3.select(document).selectAll("div");
test.ok(sel.selectChildren("*") instanceof d3.selection);
test.equal(sel.selectChildren("*").text(), "hello");
test.equal(sel.selectChildren().size(), 4);
test.equal(sel.selectChildren("*").size(), 4);
test.deepEqual(sel.selectChildren(), sel.selectChildren("*"));
test.equal(sel.selectChildren("span").size(), 4);
test.equal(sel.selectChildren("div").size(), 0);
test.end();
});

0 comments on commit 78c9409

Please sign in to comment.