mbostock/d3 — pull request #1618 by shawnbot (2013-11-07 21:52:58+00:00)
Title: Add children(), ancestor() and parent() methods to d3.selection

This overly ambitious pull request proposes some new methods for the `d3.selection` prototype:

#### selection.children([selector])
Selects just the child nodes, optionally filtering by a selector or function. So this:

```js
var foo = d3.select(".foo"),
fooNode = foo.node(),
bar = foo.selectAll(".bar")
.filter(function() { return this.parentNode === fooNode; })
.classed("baz", true);
```

Becomes:

```js
d3.select(".foo")
.children(".bar")
.classed("baz", true);
```

My primary motivation was to make it simpler to restrict selections to a node's children. Doing this outside of the d3 internals I quickly discovered that just filtering by selector meant having to reimplement `d3_selectMatches()` by finding the vendor-specific implementation. (I'm probably going to issue a separate pull request to expose that API, too.)

#### selection.parent()
Selects the parent node of each node in the selection. It's just a shorthand for `select(function() { return this.parentNode; });` (but see below for a potential gotcha that also applies to ancestor()).

#### selection.ancestor(selector)
Selects the ancestor (parent, grand-parent, etc.) of each node in the selection that matches the provided selector or function. If no selector is provided, we just return `this.parent()`. The motivation here was to make it easier to move *up* the DOM from a given selection. For instance, of you want to add a click handler to a link that toggles a specific ancestor's "active" state:

```js
d3.selectAll(".item")
.selectAll("a.action")
.on("click", function(d) {
d3.select(this).ancestor(".item")
.classed("active", d.active = !d.active);
});
```

This is particularly useful in situations where you don't have any control over how the DOM is structured, but you know the selectors of the elements you're targeting. The alternatives look like this, and they suggest either having access to an existing selection (which may or may not have changed by the time the event handler gets called) or re-selecting the containers, neither of which should be necessary (and, in some cases, may not be possible):

```js
// iterate over the whole list every time
var div = d3.selectAll(".item")
.selectAll("a.action")
.on("click", function(d) {
d.active = !d.active;
div.classed("active", function(o) { return o.active; });
});

// use selection.filter()
var div = d3.selectAll(".item")
.selectAll("a.action")
.on("click", function(d) {
div.filter(function(o) { return o === d; })
.classed("active", d.active = !d.active);
});

// stash a reference in the data
var div = d3.selectAll(".item")
.each(function(d) { d.container = this; })
.selectAll("a.action")
.on("click", function(d) {
d3.select(d.container)
.classed("active", d.active = !d.active);
});
```

**Question:** Should `selection.ancestor(function(d) { ... })` receive the data bound to the parent node (that's what I'm doing now), or should it receive the data of the current selection? The latter probably makes more sense, but I wanted to get this out there for a gut check before I spend any more time on it.

**Question:** How should multiple-element selections behave when you select their parent or ancestor? For instance, in this example, `list` becomes a multi-selection of the same `ul` element, which probably isn't right:

```js
var list = d3.select("ul")
.selectAll("li")
.classed("foo", true)
.parent();
```

Maybe `parent()` and `ancestor()` should always return a new selection of the first node's parent or ancestor (using `d3.select()`) instead of using `this.select()`?

The included tests aren't fully fleshed out, but the few that I've added pass. Let me know what you think. If children() is good but parent() and ancestor() are too complicated or problematic, I'm happy to modify or issue a more specific PR.