MatthewMueller/cheerio — pull request #308 by jugglinmike (2013-11-23 20:25:10+00:00)
Title: Dom lvl 1

From time to time, people get confused about the node-like object that Cheerio
wraps. It describes the same structure as a DOM node, but its shape is a little
different:

| DOM Level 1 Node | Cheerio Node |
|-------------------|-------------------|
| tagName | name |
| childNodes | children |
| parentNode | parent |
| previousSibling | prev |
| nextSibling | next |
| firstChild | (not implemented) |
| lastChild | (not implemented) |
| nodeType (number) | type (string) |

The "Cheerio Node" structure is certainly more concise and easier to use, but
it violates users' expectactions about the usage of the library. While it's
true that this interface is beyond Cheerio's responsibility, I don't think most
users appreciate this distinction--they call Cheerio methods, and are returned
objects with a specific shape. Besides being a little confusing, these
differences make code less portable.

`HTMLParser2` has been built to accept a custom `DomHandler` class. This means
that we could define Cheerio's Nodes to be compliant with DOM Level 1.
Unfortunately, `CSSselect` assumes the structure we currently use.

Although I think it would be great to update all our dependencies to use a more
standard API, I don't expect that these considerations (i.e. portability and
the expectations of front-end developers) are priorities there.

What we might do instead is "overload" Cheerio's Nodes with ES5 property
descriptors. This would mean that Nodes would expose both the compliant *and*
non-compliant APIs\* so users expecting a complant API would find it.

There are performance implications to this approach--creating each Node will
require a few function calls. One thing we might do is expose another option to
`$.load`, enabled by default: `withDomLvl1`, so that users that are able to
"opt out" of this behavior. Internally, we can continue to use the non-standard
API so we aren't impacted by the overhead of accessing/mutating and so that
things work as expected when users opt out.

After working with this for a while, I'm thinking it might actually make for a
good patch for `HTMLParser2`. @fb55: Would you be open to a change like this?
I've implemented this via two commits (one defines the default `DOMHandler`,
one modifies it) to help demonstrate exactly what this might look like for that
lib. Your library could disable the option by default, mitigating the
performance implications.

\* I realize this results in a *different* non-compliant API, but I'm trying to
be pragmatic :P