Dry Evented Javascript
- A css selector registry
- A sweet event delegation API
- A simple inheritance system
If you've ever written a moderately complex web application chances are you've found your self duplicating css selectors all over your javascript.
// maybe like this?
function focusPost(post){
$('#posts > .post').removeClass('focused');
post.addClass('focused');
}
$('#posts > .post').live('mouseenter', function(e){
focusPost($(this));
});
This approach can easily become a nightmare when you realize you need to tweak your markup. Selectors.js makes this easier by allowing you to register named css selectors in a tree and then easily query for them by name. This helps you dry up your code and makes updating your markup super easy. Like so:
NOTE: the code below is using the S
(as in Selector) function not the $
(dollar) function
// register two new selectors named posts and post
S('body')
.down('posts','ul.posts')
.down('post','li.post')
.end()
.end();
function focusPost(post){
S('post').get().removeClass('focused');
post.addClass('focused');
}
S('post').mouseenter(function(e){
focusPost(this);
});
Selectors.js is similar to jQuery except it operates soley on CSS selectors and never DOM nodes.
// contining with the above example
// we get the post selector
var post_selector = S('post'); //-> #<Selector: value="li.post">
// you can use it to query the dom using jQuery
var posts = post_selector.get(); //-> jQuery('html body ul.posts li.post')
// you can define additional selectors
post_selector.down('title','h3') //-> #<Selector: value="h3">
// you can query for child selectors
post_selector.down('title') //-> #<Selector: value="h3">
// can register delegated event handlers (with some nice slight changes)
post_selector.click(function(e){
this instanceof jQuery; //-> true
});
Possibly the best part of Selectors.js is the ability to extend jQuery collections of specific selectors.
S('post')
.extend({
delete: function(){
alert('deleting "'+this.down('title').text()+'"')
}
})
NOTE: the code below is using the S
(as in Selector) function not the $
(dollar) function
The S
method is used to query the selector registry. The html and body elements are defined by
default
S('html'); //-> #<Selector: value="html">
S('body'); //-> #<Selector: value="body">
S('html').toString(); //-> html
S('body').toString(); //-> html body
The following code registers two new selectors:
S('body')
.down('posts','ul.posts')
.down('post','li.post')
.end()
.end();
You can query for them like this:
S('posts').toString(); //-> html body ul.posts
S('post').toString(); //-> html body ul.posts li.post
You can be as specific as you want
S('html body posts post').toString(); //-> html body ul.posts li.post
S('body post').toString(); //-> html body ul.posts li.post
And then use them like this:
S('post').mouseenter(function(e){
S('post').get().removeClass('focused');
$(this).addClass('focused');
});
Then say later you decide to drop the post classname off the li element and just consider all immediate decedents of ul.posts a post you only have to change your selector in one place like so:
S('body')
.down('posts','ul.posts')
.down('post','> li')
.end()
.end();
The S function allows you to query the selector tree much like the $ function lets you query the DOM.
S('body')
.down('header', '#header')
.down('nav', '> ul.nav')
.down('login-button', '> li.login').end()
.end()
.down('login-form', 'form.login')
.down('login-button', 'input[type="submit"]').end()
.end()
.end()
;
S('header').toString(); //-> html body #header
S('nav').toString(); //-> html body #header > ul.nav
S('login-button').toString(); //-> html body #header > ul.nav > li.login
S('login-form').toString(); //-> html body #header form.login
S('login-form login-button').toString(); //-> html body #header form.login input[type="submit"]
If given two arguments down defined an immediate child selector. S('body').down('header', '#header') //-> #<Selector: value="#header"> S('body').down('header', '#header') //-> Error: "selector "header" already defined"
S('body').tree().toString();
/*->
: html
body : html body
header : html body #header
nav : html body #header > ul.nav
login-button : html body #header > ul.nav > li.login
login-form : html body #header form.login
login-button : html body #header form.login input[type="submit"]
*/
S('header').to('login-form login-button') //-> form.login input[type="submit"]
S('login-button').from('nav'); //-> > li.login
lets say you want all the links on your page with an href of "#" to do nothing when clicked
Selector('body a[href="#"]').click(function(event){
event.preventDefault();
});
The down function allows you to specify the name and value of child selector
lets say you had the following header and navigation menu markup
<html>
<head/>
<body>
<div class="header"></div>
<ul class="nav">
<li>home</li>
<li>signin</li>
</ul>
</div>
</body>
</html
you could define this hierarchy in javascript like this:
S('body')
.down('header', '> .header')
.down('nav', '> ul.nav')
.down('button', '> li').end()
.end()
.end()
;
now you can query for these selectors by name using the S method like so:
S('header').toString();
//-> "html body > .header"
S('nav').toString();
//-> "html body > .header > ul.nav"
S('header nav button').toString();
//-> "html body > .header > ul.nav > li"
S('header button').toString();
//-> "html body > .header > ul.nav > li"
you can also search for sub-selectors using the down method
S('header').down('nav').toString();
//-> "html body > .header > ul.nav"
Selectors.js was written by Jared Grippe [email protected]