LRBlog

Logical Reality Design: Web Design and Software Development

Archive for the ‘HTML, CSS, and Web Standards’ Category

NinjaScript: Javascript so unobtrusive, you’ll never it see coming

October 13, 2010

NinjaScript LogoWe're happy to announce NinjaScript: a jQuery plugin for unobtrusive scripting.
NinjaScript provides:

  • CSS-like language for web page behavior
  • Define rich behaviors that include both event handlers and transformations.
  • Durable behaviors that survive DOM alteration, with performance comparable to jQuery's live() method.
  • Handy built-in behaviors for AJAX.

Motivations

Unobtrusive Javascript is one of the coming movements in web design for a reason. Separation of concerns is generally a good thing, and the idea of separating behavior from semantics is pretty obvious once you think about it. If nothing else, it makes it much easier to think about how you structure your site. Just build it out as if this were still 1998 and you couldn't trust a browser to open an alert box, much less submit AJAX, then come back and mark everything up.

On the other hand, one hears a lot about "Unobtrusive Is Hard" and how a graceful degrade takes twice as long, etc etc. At the same time, software exists to encapsulate skills.  Could be you'll be seeing a Rails plugin from LRD soon to convert the big Rails helpers into degrading versions.

Therefore: NinjaScript. Unobtrusive Javascript in a tidy package so that you can get on with your day.

What's it look like?

Here's a very simple example. Suppose you have an existing

that POSTs and reloads the page, and you would like it to submit to the same URL, but via AJAX.

If you have NinjaScript loaded, this all you need:

$.behavior({
  '#coolness': $.ninja.submits_as_ajax() 
})

In addition to the pre-defined behaviors like submits_as_ajax(), you can build your own rich behaviors, specifying both transforms (alterations to an element that will be applied as soon as the element appears in the doms) and event handlers at the same time:

$.behavior({
  '.date_entry': { transform: function(elem){ $(elem).datepicker() }},
  '#work_unit_select_all': { click: selectAllWorkUnits },
 
  '#timeclock form.edit_work_unit':    $.ninja.ajax_submission({
    busy_element: function(elem){ return $('#timeclock')}
  }),
 
  '#messages .flash': {
    transform: function(elem) {
      $(elem).delay(10000).slideUp(600, function(){$(elem).remove()})
    }
  },
  '#timeclock input#work_unit_hours': {
    click: function(evnt, elem) {
      $(elem).val(hours_format(task_elapsed))
    }
  }
});

That's direct from an LRD project.

Basically, it's meant to look like a stylesheet - as much as possible within jQuery.  This snippet:

  • adds a datapicker to the date entry fields
  • binds a click handler (defined elsewhere) to allow for selecting all work units
  • makes a form into an AJAX submitter - complete with busy overlay
  • makes the #messages list decay - items live for 10 seconds and then go away
  • sets up automatic calculation of hours for certain fields

Pretty simple, but powerful.

What you're seeing there are CSS-style selectors (strictly speaking: jQuery selectors) used to pick elements, and behaviors applied to the elements. $.ninja.ajax_submission() is a prepackaged behavior, which are pretty easy to write.

The ad hoc behavior applied to '#messages .flash' defines a transformation. Transformations are basically the code you'd throw into a document.ready block, pre-sorted to go to their respective elements, with the added bonus that they'll survive later modifications of the DOM.

Behaviors can also define event handlers by adding an events clause, with the events they respond to as keys. In other words:

$.behavior({
  '.fun': {
    events: {
      click: function(ev, elem){ $(elem).sing_and_dance(); }
      mouseover: function(ev, elem) { $(elem).shiver_in_anticipation(); }
      //yes: mouseover.
    }
  }
});

What the app needs to do

To start, pretend that there is no AJAX. Build everything with full round trip HTTP.

Next, come back and make sure that your app responds to requires for javascript with scripts to do whatever you need them to do. Replace elements, usually.

Finally, add behaviors to your pages with NinjaScript. For AJAX, you don't need to change the HTML at all. All straightforward forms and GET links can be converted into to AJAX forms just by specifying the submits_as_ajax() behavior as shown in the top example above. Since you wrote them without AJAX originally, they will continue to work and degrade gracefully without AJAX.

How it works

The short answer is: rebinding. Event delegation is well and good, and if that's all you need, you probably can look elsewhere. My advice is to stick around, though. You get plenty of goodness from NinjaScript, without too much pain.

There are some problems with bubbly delegation though.  Event delegation doesn't solve the problem of modifying elements. You can't delegate watermarking.  And you can't (easily) store data on an element-by-element basis while you're delegating.

NinjaScript builds and applies behavior objects to all the elements selected in the $.behavior block. When they're applied, behaviors modify their host with their transform function (adding tooltips, changing no-input forms into links, pulling input labels in as watermarks, etc.) and apply event handlers directly to the element. They also mark the element as having been enriched with behavior, so that we don't try to re-apply behavior.

Now, when the DOM is modified, the collection of behaviors is told to reapply all the behavior objects. Any elements already enriched get skipped, since we know they were already enriched. One nifty consequence is that elements that weren't around for the initial application get found this time and get behaviors applied.

How do we know the DOM was modified? Believe it or not, there are events that a lot of browsers generate when the DOM is changed, and we listen for those. Plus, when a NinjaScript behavior does an AJAX call, it assumes that the resulting javascript execution changed the page, and it fires it's own event based on that.

Consequences of adopting NinjaScript

Most noticeably, NinjaScript event handlers are a little different from normal event handlers. We assume that events shouldn't bubble and shouldn't fire their normal behavior - you can save a little code not worrying about suppressing those. NinjaScript handlers also are called in the context of the behavior object they're attached to. This means that "this" is not the element receiving the event; "this" is the behavior object that is unique to the element. You can stash information about the behavior in there during the transform step, or maintain state for the element between events. The event handler receives not only the event record (with the original target, etc.) but also the element it's attached to as arguments. All in all, changing a standard event handler over to a NinjaScript handler isn't terribly difficult.

You should also be aware that NinjaScript really does need to know when the DOM changes. Everywhere but IE, you should be okay without doing anything - any DOMNodeInserted or DOMSubtreeModified that reaches the root node should trigger rebinding. To be safe, call $.ninja.tools.fire_mutation_event() and everything should be fine.

There exists a (small) set of utility functions at $.ninja.tools - right now there's only:
$.ninja.tools.perform_ajax_submission(form_or_anchor) - Submits form data over AJAX, evals the response and triggers a rebind.

Directions for the Future

NinjaScript really wants more stock behaviors. Already on the TODO list are:

  • make_watermark
  • Editable table rows - it'd be nice to be able to have AJAX checkboxes and draggable order
  • Fading messages - complete with a backlog and roll back - "Wait, what was that?"
  • Undoable edits - there's likely a lot of backend support this needs.

Behaviors should be mergable. At the moment, the application of two behaviors to the same element is undefined, in that their order isn't predictable.

In Defense of Sass

February 11, 2009

I've been playing with Sass and Haml in my rails projects the last few months. While I'm a bit ambivalent about Haml, I've wholeheartedly adopted Sass. A friend just forwarded me this post at fecklessmind, which excoriates Sass as a maintainability nightmare.

While I understand the guy's complaints, I have to say I disagree. I think he's complaining about a code convention that he shouldn't be following in the first place, rather than the underlying language, and he's ignoring some of the other most useful things Sass brings to the table.

Nesting in Sass

One of the most common problems I've faced over the last eight years of writing stylesheets is interfering selectors. When you have a complex cascading selector, it's often not obvious exactly where it will apply, because of the way priorities work. So a hundred times I've set some styling on UL's and LI's (thinking of ones in my #content block), only to have them accidentally interfere with the layout of my suckerfish dropdowns back in #nav.

That's an easy case, but sometimes with complex selectors it can be hard to figure out who's interfering with whom. However, once you've found the culprit, the solution is generally to go back and wrap all of the rules in an outer selector, changing all of my li {rule} selectors to #nav li selectors, or whatever. When you have twenty different rules in that section, doing this is a royal pain in the butt. Especially when you have multiple tag selectors on one line: it's seriously annoying to change the nice clean h1, h2, h3 to #content h1, #content h2, #content h3!

When you do need these wraps, Sass makes it super easy via auto-nesting:

#nav
li
:color #whatever
:float left

a
:whatever etc

will compile to:
#nav li {
color: #whatever
float: left
}
#nav a {
whatever: etc
}

Now, the author of fecklessmind is complaining about how this makes rules harder to find, and how it slows down parsing. Both of these can be true, if you overdo it. But I don't - Sass doesn't force you to wrap your rules this way, and I frequently don't when it doesn't provide any benefit or when it would cause me to write redundant rules. I can and frequently do write single-line cascading selectors, and rules without wraps at all - the very things fecklessmind is complaining that Sass takes away from him.

Nothing about Sass prevents me from writing things like this:
body #nav ul li a
:float left

or even
#content h1, #nav h2, .article h3, p h4
:font-weight bold

If that's really what I want to do. I learned how and when to wrap selectors with a near-decade of writing CSS, and I apply those same guidelines when I write Sass - Sass just makes it easier when I do want to do it.

The benefits of nesting early

While I don't use Sass nesting everywhere I possibly could, I do often use it slightly more than would be absolutely required.

The reason is that it heads off a lot of annoying bugs with interfering selectors. For example, say a rule I wrote for .article .body p, and it's not getting applied. After some sleuthwork (long, painful, frustrating sleuthwork if I'm on a platform without firebug, like IE), it turns out this is because there's a #content p rule 2000 lines earlier in the CSS file that's obscuring it. When I nest things in Sass to create a clean cascade hierarchy, this kind of interference is far less likely to occur in the first place.

Meanwhile, the other benefits of Sass

CSS is riddled with problems, and Sass solves two of the most egregious: magic numbers/constants, and compiled server-side imports.

Eliminating magic numbers in CSS

For constants, Sass lets me define commonly used tokens (like colors, for example), and reuse them throughout my stylesheets. This means if I want to adjust a color, I can change it in only one place and the result is reflected throughout my code. Very handy:

!main_link_color = #48950a

a
:color= !main_link_color

#content h1
:border-width 0 0 1px 0
:border-color= !main_link_color

Now, if the client says "make the links blue, not green", I can change that constant and it gets automatically reflected everywhere else. Brilliant.

Organizing my code

fecklessmind says this:

... imagine that the stylesheet is 5000-lines long and you’re looking for p selector, rather than #article. In classic CSS you could just search for #main p, but in Sass they are miles apart. Swell, isn’t it?

A five-thousand-line line file? You're doing it wrong. No code should ever look like that. CSS is the only major language that compels you to work that way and Sass fixes it.

Every good programming language lets me put my code across multiple files, in a nice, organized heirarchy. One class per file and all that: essential for readability and maintainability. But if I use CSS, I can't very well organize my stylesheets into multiple files. If I do, I have to import them client-side, which generates extra hits for the user's browser and extra load for my server. As a result, CSS files tend to be monolithic multiple-kiloline monstrosities.

Sass fixes this. If I use @import to import a Sass file into another Sass file, Sass automatically and transparently compiles that server-side and ships out a single file to the user.

The result is that, writing Sass, I often have 20-30 files containing only a page or so of code, each for a specific feature or layout section. The client still only sees screen.css (and maybe print.css, mobile.css, and ie6.css), but screen.css contains the compiled contents of layout.sass, nav.sass, links.sass, content.sass, footer.sass, etc. In case I need to scan through the compiled screen.css and figure out where a rule came from, I start each file with a single comment containing the name of the file; /*------ nav.sass and such.

If the rule is for paragraphs that could appear anywhere in #main, in my Sass code it would be a file called main.sass, which is usually a relatively short file; 50-60 lines. (All the things destined for other elements would appear in articles.sass, or calendar.sass, or data_tables.sass, keeping main.sass short for only the universal elements).

That logical grouping — the way every other programming language does it — helps me find my CSS rules much more quickly, I think, than fecklessmind's "search for one-line selectors" would. Because with his approach, I might think I'm searching for #main p, when in fact what I really want is #main .section p, and thus my search won't find it.

In reality, there's no way to make a single 5000-line file easily maintainable, period. fecklessmind's little tricks are just that: tricks built from years of experience working in a broken system. Better to use logical organization to solve the problem, and Sass lets me do this.

The bottom line is, badly-written Sass could be horrible to maintain, and maybe worse in some ways than badly-written CSS (but better in others, particularly in weird cross-reactions between unwrapped css selectors). But the same is true of badly-written code of any type. And in my experience Sass gives me much better tools to write maintainable stylesheets than CSS alone does.

Firefox Download Day (= display: inline-block day!)

June 18, 2008

It's Firefox 3 Download Day!   Go get it, please.

I am excited about this, but perhaps not for the same reason most other people were. I am excited because Firefox 3 is the first version to implement the CSS rule "display: inline-block". Inline-block allows you to generate chunks of content of rectangular shape that will flow inline in your content (like text, images, or tables) but whose contents act like a block, and whose borders and margins are respected in the flow.

Let's make it clear what I mean.

The beauty and versatility of display: inline-block

Setting a CSS element's display: property to inline-block tells the browser to treat the element as a rectangular block, with internal flow just like any other block, and to respect the padding, border, and margins, but to flow that rectangle with other inline content - exactly the way the browser would flow an image. In addition, you can specify the width and height of inline-block items. This is a crazy useful concept from a layout perspective: it would allow you to make buttons out of text that lay out horizontally, or to layer images on top of each other inside a small span or div.

Here's an example of a series of list items set with border, padding, and a width and height. The code first:

<style>
   ul {
	width: 400px;
	border: 2px solid black;
	background: #aaf;
	}
li {
	border: 2px dotted blue;
	padding: 1em;			
	width: 100px;
	height: 35px;
}
ul.inline li {
	display: inline;
}
ul.inline-block li {
	display: inline-block;
}
</style>
<h2>Padded words wrapped with display: inline;</h2>
	<p>
		<ul class="inline">
		<li>Lorem</li>
		<li>ipsum</li>
		<li>dolor</li>
		<li>sit</li>		
		<li>amet</li>
		<li>consectetur</li>
		<li>adipisicing</li>
		<li>elit</li>		
		<li>sed</li>
		<lido</li>
		<li>eiusmod</li>
		</ul>
	</p>
 
	<h2>Padded words wrapped with display: inline-block;</h2>
	<p>
		<ul class="inline-block">
		<li>Lorem</li>
		<li>ipsum</li>
		<li>dolor</li>
		<li>sit</li>		
		<li>amet</li>
		<li>consectetur</li>
		<li>adipisicing</li>
		<li>elit</li>		
		<li>sed</li>
		<li>do</li>
		<li>eiusmod</li>
		</ul>
	</p>

Here's how Safari renders the above code (correctly):

And here's how Firefox 2 renders it (incorrectly):

Horrific - FF2 simply ignores inline-block, causing the list elements to render in their default display mode, block. What's galling about this is that for several years Firefox has been the only browser that gets it wrong! That's right, even Internet Explorer 6 gets this right ... at least on some elements, specifically those that are natively display: inline, like spans and anchors.

The Firefox bug report was filed way back in the days of Firefox 1 nine years ago. Yes, you read that right. It sat there, unfixed, while every other browser in the world implemented it.

Anyway, today web developers everywhere can rejoice. Now, at last, the released version of every major browser supports display: inline block. So, with only another year or two waiting while FF2 users upgrade to FF3, designers will be able to start using this singularly useful CSS rule (at least in cases where IE supports it as well).

A parting example

A parting example of the kind of cool trick you can play with inline-block: text flowing inside a sized box that is itself flowing inside text.

Code for flowing text inside flowing text:

<div style="font-size: 250%; width: 600px;">
	Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor 
		<span style="width: 130px; height: 40px; font-size: 9px; display: inline-block;
			padding: 1em; border: 1px solid black; overflow: hidden;">
			Duis aute irure dolor in reprehenderit in voluptate velit esse cillum 
			dolore eu fugiat nulla pariatur.
		</span>	
	incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam. 	
	</div>

How it renders in a compliant browser ... including IE6!*

Tricks with flowing text

*Well, more or less. IE6 gets it reasonably close.

Another new design for LRDesign.com

May 29, 2008

Apparently I can't stop playing, because today I built and uploaded a third design for the LRDesign main site: "Blue Art". This one required the IE PNG fix from Twin Helix for IE 6.0 compatibility.

I think there are still a few annoying bugs in the stylesheet switcher on IE; once I clean those up I plan to write a post explaining how to easily set up this kind of stylesheet switcher in a Rails project.

New theme added to LRDesign.com

May 29, 2008

For quite a while I've wanted to decorate the main Logical Reality site with multiple, switchable themes, implementing each with pure CSS on top of semantic HTML markup, a la the CSS Zen Garden. Last night I finally got a start on that, implementing a simple stylesheet switcher and a grunge theme with textures from Urban Dirty.

To check it out, load the main site and click the "Change Skin" link in the upper left.