Friday, June 19th, 2009

NodeIterator.areYou(IMPRESSED | WHOCARES | WHA?)

Category: JavaScript

<p>John Resig has posted on the DOM traversal methods now in Firefox 3.5 and then a spin out post on the merits of the NodeIterator API. He isn’t impressed:

This API is, at best, bloated, and at worst incredibly misguided and impractical for day-to-day use.

Observe the method signature of createNodeIterator:

javascript
< view plain text >
  1. var nodeIterator = document.createNodeIterator(
  2.   root, // root node for the traversal
  3.   whatToShow, // a set of constants to filter against
  4.   filter, // an object with a function for advanced filtering
  5.   entityReferenceExpansion // if entity reference children so be expanded
  6. );

This is excessive for what should be, at most, a simple way to traverse DOM nodes.

One part of the critique involves the common pattern of bitwise operators that are common in C, C++, and a bit of Java (and elsewhere too). When space is at a premium, these are a good choice. There are also some nice side effects when you use them (building up the flags, munging them later, etc).

However, as John points out, these are more for CSci students than for the average Web developer.

But then the crazy comes in: In order to select multiple, different, types of nodes you must OR together the properties to creating a resulting number that'll be passed in.

For example if you wanted to find all elements, comments, and text nodes you would do:

javascript
< view plain text >
  1. NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT

I'm not sure if you can get a much more counter-intuitive JavaScript API than that (you can certainly expect little, to no, common developer adoption, that's for sure).

He goes on to propose some more Webby APIs such as:

javascript
< view plain text >
  1. document.getNodes( Element, Comment, Text );

Posted by Dion Almaer at 2:09 pm
14 Comments

+++--
3.3 rating from 28 votes

14 Comments »

Comments feed TrackBack URI

While I agree entirely on the nodeIterator implementation I don’t understand the fundamental opposition to bitwise combination of function arguments.
Sure, javascript functions can take any amount of arguments, but is that notation really such a big deal? Even for someone who does not get one bit of the math behind those operators… its simply using “|” instead of “,”. :P
Also, anyone who wants to write any code that even remotely works reliably MUST have at least a basic understanding of logic. The most beautifully designed API cannot change that.

Comment by carrion — June 19, 2009

Hmm. I think a bitwise or for flags is fine. I’m assuming a plus would work as well. I don’t see what the big deal is, but I’ve worked with bitwise flags in many languages and it’s never stumped me.

Comment by Nosredna — June 19, 2009

So you’re proposing turning one function into three or more just because the average “developer” is more used to using a comma than a pipe? Nice.

Comment by Darkimmortal — June 19, 2009

Since “FOO | BAR” translates in English into “FOO or BAR” there is a synergy that you don’t have with “FOO, BAR”. Although, I can see non-bitwise people making the mistake of typing “FOO || BAR” and the program running without error but not behaving as expected since it would eval to just FOO.

Comment by greim — June 19, 2009

@carrion, Nosredna, Darkimmortal, greim: you guys seem to be missing the point.

The NodeIterator API is just a pain to work with, especially if you have a language supporting first-class functions. Imagine having to write:

document.createNodeIterator(document, NodeFilter.SHOW_ALL, null, false);

Isn’t it too verbose? Isn’t it too much for such a simple task? Furthermore, at least one parameter (whatToShow) is unnecessary, because it could be defined using the other (filter) quite easily.

I would like to stress that NodeIterator API is analogous to filtering and mapping over a list. Why oh why invent yet another method for doing that? It’s simply dumb and counterproductive.

DOM sucks for the particular task of web scripting, lets face it.

Comment by chiaroscuro — June 19, 2009

By Mozilla’s own admission, the NodeIterator has been implemented for easier traversal of hard to traverse nodes like comments or textnodes. It’s for fringe cases AND you can use it for other node traversal IF you want to use it for that purpose.

That said, the NodeIterator API fails at clean code, fails at verbosity and could’ve been implemented smarter (even if it meant writing more lines of code).

A more robust multifunctional Iterator that works for all objects (not just DOM nodes) with a filter method for precision (filter objects on attributes, in this case nodeType) would be better. Leave it to the developer on how he uses or extends it. Don’t go and create ninja methods just for the DOM. The DOM should just be another object available. If traversing the DOM causes performance issues, improve the DOM implementation itself, not the API.

Comment by BenGerrissen — June 20, 2009

this is hilarious, another pea-counting method with frumbulous parameters from the ECMA world. yuck. see, in javascript you can’t, to this day, as much as ***iterate over a list*** without writing convoluted code. yes shut up it’s convoluted! `var count = my_list.length; for( var i = 0; i < count; i++ ) { var element = my_list[ i ]; … };` is F*KING CON-VO-LU-TED!! and yes i know about `for( x in y )` and its BROKEN and doesn’t help a bit!! and its awful syntax to boot. this is 2009, do you think we’ll ever get to mars with this idiom?

great now they come up with another OOP-inspired `foobar.dothatforme( mystery, secret, attention, additive, flap )` method definition that no-one on this or the next planet really needs. or would have thought out or put into a bloated standards document IF JAVASCRIPT WAS A BLEEDING SANE LANGUAGE which it is NOT NOT NOT. it is SICK and you won’t heal it by adding any `magic(bs)` method to it YOU CANNOT EVEN CALL IN 60% OF ALL PAGE REQUESTS WITHIN THE NEXT FIVE YEARS! who is supposed to care?

John thanks for giving the world jQuery and actually being one bright mind who cares to look into the mess that is the theory and practice of browser scripting. yesterday i wrote a module to capture user keystrokes that can actually record what they typed. you see, this is such a mess. you get keypress, keydown, keyup events. they’re not consistent across browsers. some browsers return keycodes that differ from others. no browser tells you, in the event, what was actually typed. starngely, any dumb form text input can do that; you hit a key on your spanish or french or japanese keyboard and boom it goes into the text field and shows up. ONLY BLEEDING JAVASCRIPT INSISTS ON SPAMMING YOU WITH WORTHLESS CONTRADICTORY GARBAGE DATA that purports to BUT DOES NOT reflect anything going on in the real world on the real keyboard. hell those events want to tell you the character was `r` when the user hit F3 and `.` when the user hit delete. it pretends to have seen an `A` when the user hit `a` and `Þ` of all letters when i hit `ä` on my german keyboard. this IS BROKEN BEYOND REPAIR by any standard.

now i get ANOTHER METHOD with an API THAT MAKES ME SHOUT AND CRY that is WORTHLESS for the foreseeable web-future (years) and does NOTHING in terms of general data processing. if they only stopped to be so OOP-obsessed and start to put their data into the focus! (hey, even the dom is just so and so many references to nodes, right? i can represent it with so and so many lists and hashes filled with strings and numbers, no? so why not come up with a generic lists-and-hashes-filtering model? but this is not the way of javascript!)

sorry for the shouting. i had a hard day. now i feel better.

Comment by loveencounterflow — June 20, 2009

ok one after-burner. john says, the API discussed is “at best, bloated, and at worst incredibly misguided and impractical for day-to-day use”.

yes. and why so? why can we, in 2009, recycle so little of our code? one reason for this may be that we are caught in the OOP specialization trap. you see, in OOP, you stuff all your pertinent details into the guts of a specialized object that has a type, or class, whatever. sure, it is only with dom elements on a web page that you want to ever slideUp, slideDown (not in the dom but john did it. very handy, thank you for that one, and i mean it. javascript starts to make sense with jQuery).

sure, it is only on a web page that you ever want to `document.getThatElementByItsId()`. BUT that element, and that document, aren’t they just collections of data? quite general data? i mean, general as in JSON? strings of text? numbers? booleans? either sequences of those or accessible by names, concept called h-a-s-h?

so when you start to see that, you realize OK, maybe only a dom element can really slideUp, slideDown, so that is specific. som elements *are* indeed a little special in that they have (typically) a live, dynamic representation inside the browser viewport. but other than that they are just DATA, people. anyone not believe me i then ask them how comes we can reify a web page—a dynamically changing one, on top of that—by merely sending a single HTML file—pure, structured text, that may include or invoke snippets of (pure text) CSS, javascript etc—to the client? see?

the other day i wrote a method so you can click on that web page and have the current graphical content of a canvas stored, as a png, on the server. it is transmitted as text. no image per se, just so many As and Bs and Cs going over the wire. you see so and so many shiny thingies there and it is but data, structured data, text. think matrix; blond, brunette, or black, just characters of data floating by.

ok so when we get to that point and agree it is just so and so much very general data each with a few special (but then again, mostly *typical* as in type, or class) properties—like this line lives in my log file, that portion makes a red square inside the browser, here is a song by britney spears, there the likeness of robbie williams—we also ought to understand that this generality, which unites all of data as we can perceive of it now, makes up the greater, and more important part; that the specialty (that i can, say, represent a text in all upper case, lower the volume of a sound recording, or enhance the contrast of an image) is but a fraction of all the interesting things i can do with that data. the fine people who thought out the first filesystems were quite aware of this (and put it to extremes in plan9).

now the point is: javascript in particular, and much of the OOP-driven world in general, acts if there never had been a filesystem. seriously. they invent bloated APIs so now we can count green peas in addition to the yellow ones of yesteryear. great. i say: there is another method, that intends to facilitate dom node filtering and traversing, not bad in itself and for sure, in this world, being sought after (for they, very capable! at that, mess that javascript has become). also a logical consequence, given the precedence.

but: now we get this method, along with a little booklet describing its plethora of call arguments (unnamed! to this day all javascript function arguments are UNNAMED FOR CHRISTS SAKE! A-N-O-N-Y-M-O-U-S! you get a letter by anonymous it sure is spam, blackmail, or worse! think of unnamed function arguments that way!), and it for sure (if and when browser vendors get around to bring it to your clients’ desktops) will make our lives a little “easier” (and heap up more things to know, and more rules—where are the features that got discarded to keep the balance? none? production without deconstruction spells glut).

however, say i have a collection of data ['a','b','c'], will i then be able to partake of that fresh-fangled method to sort out *my* piece of data? NO! why so? well it’s just a dom method you see. so go create a dom element collection—possibly of type document—and you can then call x.createNodeIterator() on it. see how sick this is? the bane of OOP (i used to be an OOP fan, and use it every day) is really that you are forced to permanently slip into different suits—rather, make your data slip into this, into that glove—to perform different tasks. in other words, there is an iterator implemented, code got written for it that users installing a browser will get, inside of an ever growing installer download size, onto their machines—but the web page author can not put it to use *except* for dom elements. if that method really does something really useful, then before long someone will wrap by defining a function that reifies / casts a list or whatever into a faux dom structure, then applies the method, and finally re-casts the result in more mundane lists or hashes, to return it (i am not sure you can do that with an iterator in javascript though; in python it is not difficult).

to bring this home and end a second long post on this chilly and windy berlin summer afternoon: consider the use case appreciation of document.createNodeIterator(). it might be (1) useless; or else (2) useful for either (2a) dom elements and very few things else only, or (2b) a sizeable class of data constructs; or else (3) handy but easily imitated by a few lines of code. if cases (1) or (3) apply, then this latest addition to the dom is inconsequential in the big picture. now maybe the people over at mozilla hit upon something that makes only sense in the world of dom (2a), and they rightfully made it a method of `document`.

my suspicion is, however, that (2b) applies—it does in 80% of cases and this looks like one. we can then see that the OOP paradigm, while it does not forbid to implement the function as a method of the `document` object class, also does nothing to help the general public here. it is an old and quaint discussion in OOP: should it be `length(x)`? or `x.length()`? or `x.length`? is it `-`.split(‘a-b-c’)`? or `’a-b-c’.split(‘-’)`? or `split(‘a-b-c’,'-’)`? different languages settle on different answers—python has `len(x)` where javascript says `x.length`. both ways are fine, but there is a catch: if `x.length` is solely implemented on lists, and can’t be retrofitted to, say, hashes, then you will be forced to say something like `myhash.keysAsList().length` or `asList(myhash).length`—you got to cast the hash (or part of it) as a list before you get to know a useful property of the data at hand. if `len(x)` does not accept hashes, similar side-steps have to be danced. python lets you define a ‘magical’ `X.__len__()` method on any class that will be used when instances appear inside a `len()` call; but since it does not let you easily retrofit / monkeypatch such cruft on existing built-ins (you cannot make all strings or hashes, or lists magically acquire a new method, or a changed method, in retrospect; javascript and ruby both allow that, which may be good or bad), it is good luck someone did implement `dict.__len__()` for us. summing up, we always have the problem that there is some data with a certain shape, and some known ways to massage and transform and excerpt it, and someone has to bring the right ways and the data together. OOP wants to tell us that the best practice is to bundle the methods together with the specially-shaped data. what classical OOP instructors miss out on, in my view, is that they do not encourage software writers to keep their data in as generic structures as possible (and keep the directory of accepted generic structures small). instead, people are instructed, when they model a car, to make a car: `var c0 = new Car(wheelcount, doorcount, gallonspermile)`, and when they model a cat, to make a cat: `var c1 = new Cat(‘black’)`. they ensure us that when the need should arise and, say, the functionality of `dom_element.createNodeIterator()` would be handy for both a car and a cat, we can still decide (1) to derive both the Car and the Cat class from the Dom class; (2a) recast cars and cats as dom instances, then call the dom method on the new instance; (2b) wrap the recasting business in a method of both cars and cats, so users get shielded from the noisy boilerplate; (3) establish, where technically advisable, a compositional attribute `asDom` for cars and cats so users can call `car.asDom.createNodeIterator()`. all three ways have their merits and demerits; be it said that, while solution (1) constitutes the classical soap box elevator pitch for OOP, it is the one that is in practice often the most difficult to actually ‘do’—when was the last time you actually understood a given class hierarchy in java and were able to type in the class constructor factory invoking dereferencing facility incantation from memory, without the help of a bloaty-bloaty eclipse-ish IDE?

in practice we need much less inheritance than OOP would have us to believe. why? because it creates ever more very specialized objects. there is an entire universe of pretty general data transformation knowledge out there, waiting to be explored and applied. tiny bits of it can nowadays be downloaded from the internet, and it’s already terabytes of procedural code. too bad when it turns out that if you want to make the cars and the cats of your new browser game walk and quack like a duck, many of those implementations will not accept data structures that fit into a general description (like, ‘a list with at least two members, all of which must be hashes’; ‘a hash whose values are integers’; ‘a list with exactly three numbers, or null’ ), but have instantiation APIs that make grown males sob (an example from box2d-js, which can help you make your cars and cats move like physical objects: `var bodydef_b2d = new b2BodyDef(); var circle = new b2CircleDef(); circle.radius = radius; circle.friction = 1; bodydef_b2d.AddShape( circle ); bodydef_b2d.linearDamping = 0.1; bodydef_b2d.linearVelocity.Set(100,100); bodydef_b2d.SetTarget(new b2Vec2(20,35) ); var body_b2d = world_b2d.CreateBody( bodydef_b2d );`—see what hoops you have to jump through? this is only slightly worse than your average OOP-based API. shudder).

suddenly it is not all about the cars and the cats anymore, it is all about serving up API incantations that come straight from heck. when you look under the hood, then all what box2d-js does is manipulate collections of figures, data—in a clever way so objects start to behave. what consumers of that knowledge have to do in order to reap the benefits of such software is not so hot. and guess what. javascript is prototyping OOP, right? since box2d-js was ported from C, that didn’t fit so some clever guy made it work using prototype, so you get classical inheritance in javascript. it is just to organize their code, you know. it is in the book. turns out that when you run your firebug profiling on the code that does your advanced 2d physics engine stuff on the browser with dozends of objects—wow!—well, most cycles are burnt IN THE PROTOTYPING FACILITY! it is not that in 2009 we can’t simulate 12 twodimensional bodies in a flat world in javascript, but when we do, we spend like 60% of the time in a module that does NO-THING but administrate a code organization that, all at the same, imposes a horrible API onto people and gives a shit about the pertinent data.

so whatever you do to organize your code: my idea is that you do that up front, and make it keep a low profile at run-time. argue that box2d-js is a bad example all right, may-be; but they did it according to the book. how many articles have i read, by very knowledgable people, how to tweak javascript so you can do classical inheritance? now that i use it seriously for the first time and have the figures on the desk, i find it appalling, for lack of a better word.

Comment by loveencounterflow — June 20, 2009

“@carrion, Nosredna, Darkimmortal, greim: you guys seem to be missing the point.” Well such a big deal was made about the bars that it was hard to see the point.

Comment by Nosredna — June 20, 2009

I have NO idea what lovencounterflow was talking about in his two posts, I fell asleep about 10 paragraphs into it… but anyone that can write that much must be onto something, so whatever that something might be… WHAT HE SAID!

;)

Comment by fzammetti — June 21, 2009

loveencounterflow:

in javascript you can’t, to this day, as much as ***iterate over a list*** without writing convoluted code.

Maybe you want to take a look at Array methods, especially the iteration methods.

Comment by Menno — June 22, 2009

I find it quite disconcerting that someone can take a look at an object model or a browser quirk and extrapolate that to a deficiency of a language. Not only that, but spend 20+ paragraphs bitching about his perceived conclusion… God knows that ignorance won’t stop anyone from having an opinion.

Comment by TNO — June 22, 2009

@Menno: thanks for pointing this one out; from the docs, “Array.forEach(); Implemented in: JavaScript 1.6 (Gecko 1.8b2 and later); ECMAScript Edition: none”. this is not in the standard, but maybe implemented widely enough. it still is convoluted—to me—since you have to wrap another function inside a call so you get the extra braces etc for what could ideally be `for(var e in mylist){}` or `for(var i,e in enumerate(mylist)){}`. i know you can, right now `for(var i in mylist){}`, but i heard people strongly argumenting against it—presumably because the moment mylist gains a new `mylist.foo=42` that `foo` will also be iterated over. if that is not a deficiency of the language specification i don’t know what else could be. `mylist.forEach` seems to skip that extra `foo`, which is good, but also a bit confusing. in the end, `forEach` is a bit bolted-on, no?

@TNO: correct in principle and your remark on comment length is fully justified. ok for a rant it is aimed at too many targets. in short, i argue for simpler, more literate and less specific models and APIs. in place of the very funny way that the very specific `createNodeIterator` wants to work, there could be a generic filter function that works on a lot of disparate data. going to lengths to detail a specific API that happens to have caught my attention is certainly a defect, given i was only commenting. it does share some of the mumbly-fumbly convolutivity of `createNodeIterator` though.

@fzammetti: i am all pro simpler, more literate and less specific models and APIs. didn’t mean to bore you.

Comment by loveencounterflow — June 23, 2009

@loveencounterflow: using classical inheritance wrappers in JS is twisting up the language, don’t take that for an example. Javascript has a very powerful set of features, but it’s almost always misused. You have to learn it as it is, if you don’t like it, don’t use it! :)

Comment by ricardobeat — June 23, 2009

Leave a comment

You must be logged in to post a comment.