From 78c9409eea8897341c8ea0cd925c0528ae03b508 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Sat, 25 Apr 2020 12:43:55 -0700 Subject: [PATCH] Add selection.selectChild[ren]. --- README.md | 8 ++++ src/matcher.js | 7 +++ src/selection/index.js | 4 ++ src/selection/selectChild.js | 18 ++++++++ src/selection/selectChildren.js | 18 ++++++++ test/selection/selectChildren-test.js | 65 +++++++++++++++++++++++++++ 6 files changed, 120 insertions(+) create mode 100644 src/selection/selectChild.js create mode 100644 src/selection/selectChildren.js create mode 100644 test/selection/selectChildren-test.js diff --git a/README.md b/README.md index b74fe1a..6015ee6 100644 --- a/README.md +++ b/README.md @@ -204,6 +204,14 @@ This method is not intended for concatenating arbitrary selections, however: if Returns the selection (for symmetry with [transition.selection](https://github.com/d3/d3-transition/blob/master/README.md#transition_selection)). +# selection.selectChild([selector]) · [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. + +# selection.selectChildren([selector]) · [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. + # d3.matcher(selector) · [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: diff --git a/src/matcher.js b/src/matcher.js index e6d4f50..854b0d9 100644 --- a/src/matcher.js +++ b/src/matcher.js @@ -3,3 +3,10 @@ export default function(selector) { return this.matches(selector); }; } + +export function childMatcher(selector) { + return function(node) { + return node.matches(selector); + }; +} + diff --git a/src/selection/index.js b/src/selection/index.js index 077087b..a593a21 100644 --- a/src/selection/index.js +++ b/src/selection/index.js @@ -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"; @@ -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, diff --git a/src/selection/selectChild.js b/src/selection/selectChild.js new file mode 100644 index 0000000..d042794 --- /dev/null +++ b/src/selection/selectChild.js @@ -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))); +} diff --git a/src/selection/selectChildren.js b/src/selection/selectChildren.js new file mode 100644 index 0000000..a1d0836 --- /dev/null +++ b/src/selection/selectChildren.js @@ -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))); +} diff --git a/test/selection/selectChildren-test.js b/test/selection/selectChildren-test.js new file mode 100644 index 0000000..ac5adf8 --- /dev/null +++ b/test/selection/selectChildren-test.js @@ -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("

hello, world!

"); + 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(` +
hello, world!
+
hello2, world2!2
+ `); + 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("

hello, world!

"); + 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(` +
hello, world!
+
hello2, world2!2
+ `); + 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(); +}); +