$() syntax for Dart
Posted by David Chandler on December 14, 2011
Bob Nystrom’s article Improving the DOM introduces a jQuery-like syntax for Dart, but you still have to write out document.query(…). Can anything be done to shorten it? It turns out that $ is a valid function name in Dart, so, as suggested by Jacob Richmann on misc@dartlang.org, you can define a utility function like this:
ElementList $(String query) => document.queryAll(query);
Now in your UI code, you can write simply
$('h2').first.text = 'Heading 2';
This got me thinking. What if you wanted to emulate jQuery even further, like:
$('h1').addClass('blue'); // call methods on matching elements $('h1').each((Element e) => e.text = "Heading"); $('h1').onClick((Event v) => window.alert('hi'));
Here’s a tiny library that lets you do the above. It’s just a starter, but hopefully shows some of Dart’s potential for convenient DOM manipulation.
#library('dartQuery'); #import("dart:html"); typedef void ElementFunction(Element e); /** * jQuery-like syntax returns an ElementListWrapper on which methods can be called */ ElementListWrapper $(String query) => new ElementListWrapper(document.queryAll(query)); /** * jQuery-like interface providing convenience methods which * operate on multiple Elements returned by document.queryAll() * A TODO in dart:html's Element.dart suggests that some methods * may eventually make it into Dart's ElementList, possibly eliminating * the need for a wrapper. */ class ElementListWrapper { ElementList _elements; ElementListWrapper(ElementList this._elements); // invoke a function for each element void each(ElementFunction f) => _elements.forEach(f); // add a style classname to each element void addClass(String className) { each((Element e) => e.classes.add(className)); } // add a click handler to each element void onClick(EventListener h) { each((Element e) => e.on.click.add(h, true)); } // allows $('#id').first Element get first() => _elements.first; }
Warning: this was working earlier in frog and dartc, but something seems to have changed this afternoon. Either I broke it while reformatting for the blog or my Dart build is hosed. YMMV.
sakesun said
I’m not familiar with jquery, but if the return value of “$” depends on the number of element returned, wouldn’t that make the program fragile to changes in the html ?
David Chandler said
Yikes, I’ve not really used jQuery, either. Upon closer inspection, things that come after the $ selector are always methods that are part of the jQuery API. So the $ function above should instead be implemented simply as
ElementListWrapper $(String query) => document.queryAll(query);
The dynamic return type could be useful, but yes, very brittle. I’ve updated the post accordingly.
David Chandler said
Make that
ElementListWrapper $(String query) => new ElementListWrapper(document.queryAll(query));
David Chandler said
Update Dec 21, 2011: the sample dartQuery lib above works in the VM (Dartium) and dartc from Dec 1. Newer dartc in the latest editor fails, as does frog. I’ve filed issue 922 on this.