Posts Tagged ‘html’

Isotope – Hybrid Ruby on Rails Template Engine for Client Side and Server Side (Updated)

Monday, March 7th, 2011

Isotope on Github ]

Update: Jul 23th, 2011

Updated to Ruby 1.9.2 with ArthurN‘s amazing fork.

Update: Apr 13th, 2011

Updated API, external script support and fixes.

The problem:

In Ajax-based sites, there’s a constant dilemma: How to get objects rendered in templates? In server side (and output full HTML)? Client side (and mess with JSON objects and HTML strings/DOM generation)?
What should be the role division between server and client?

Common Approaches, Pros & Cons:

A few approaches to output a rendered template evaluated with an object are:

Approach #1: Regular ERB Partial

Evaluate a simple ERB partial with a local object, and server it as a string to the client, simply by

<%= render :partial => "article", :object => @article %>

in a view, or from a controller and request it by Ajax.

This partial can look like:

<h2><%= article.title %></h2>
<div class="content">
<%= article.content %>
</div>
<ul class="tags">
    <% article.tags.each { |tag| %>
    <li><%= tag.name %></li>
    <% } %>
</ul>
Pros
  • Simple, readable and well known ERB for Rails/Sinatra
  • SEO and accessibility – HTML code is downloaded into the source
Cons
  • Server side only
  • Requires download of the whole HTML code, can cause performance issues
  • Cannot bind easily to a different object on client side. Must re-rendered in the server-side and be downloaded

Approach #2: Client Side EJS Template with JSON Objects

Having an EJS template in the HTML code, with techniques such as John Resig’s JavaScript Micro-Templating

<script type="text/html" id="article-template">
<h2><%= item.title %></h2>
<div class="content">
<%= item.content %>
</div>
<ul class="tags">
    <% item.tags.forEach(function (tag) { %>
    <li><%= tag.name %></li>
    <% }); %>
</ul>
</script>

Query the server for a JSON article and evaluate the template with this object into a string, and place it inside a container, using a technique as mentioned:

var results = document.getElementById("results"); // some container on the page
results.innerHTML = tmpl("article-template", article); // article is an object, probably a result of an AJAX JSON request
Pros
  • Fast – requires the server to send only the JSON object and the HTML is downloaded only once as a template
Cons
  • SEO and accessibility – HTML code isn’t in the source of the page but being rendered after load

Approach #3: Regular ERB Partial With Marked Spots for Data Place Holders

This approach tries to combine server side and client side but requires a lot of work. It contains a regular ERB template and place holder markers like class names on elements.
The template can be first evaluated on the server with a Ruby object and on the client side it can be evaluated with a different JS object (probably from a JSON request).

Template file should look like:

<h2 class="data-title"><%= article.title %></h2>
<div class="content data-content">
<%= article.content %>
</div>
<ul class="tags data-tags">
    <% article.tags.each { |tag| %>
    <li><%= tag.name %></li>
    <% } %>
</ul>

And then, from a JS function, doing something like:

// article is an object probably from a JSON request
container.querySelector('.data-title').textContent=article.title;
container.querySelector('.data-content').textContent=article.content;
var tags=container.querySelector('.data-tags');
tags.innerHTML="";
article.tags.forEach(function (tag) {
    tags.appendChild(document.createElement("li")).textContent=tag.name;
});
Pros
  • One template for both client and server (not really, see Cons)
  • SEO and accessibility – HTML code is downloaded into the source
Cons
  • On the client side – Must mimic the Ruby functionality with JS when it comes to loops, conditions etc. However, text values are pretty easy to embed. This code should probably written manually for everything that is not a simple textual content.
  • Must maintain data place holder markers

So…

In these three approaches, the developer needs to choose according to the task and the project requirements, or worse, maintain two templates, ERB and EJS.

Each approach is written in a totally different way, and switching between the approaches means a lot of work.

Introducing: Isotope – Ruby Hybrid Template Engine for Client Side and Server Side

So why not combining all the pros of the approaches together?

The biggest constraints to be considered are:

  • Client side doesn’t understand Ruby
  • Ruby can’t be translated fully into JavaScript
  • And the most important one: Template should be maintained in one single file for both client and server uses

Isotope is from greek – “Equal Place”. An equal place of editing a template for both client and server (Thanks @yuvalraz for the name!).

Using jbarnette‘s AWESOME Johnson gem, Ruby and JavaScript can interact together!

That means, that ruby code can handle EJS templates and JSON objects. A great and very inspiring article is Write your Rails view in… JavaScript? by Aaron Patterson.

In this approach, only one template is written and maintained in an EJS format, for both client side and server side.

Usage

Isotope gives the ability to have a single template file, and easily switch between the approaches:

# article.html.ejs
<h2><%= item.title %></h2>
<div class="content">
<%= item.content %>
</div>
<ul class="tags">
    <% item.tags.forEach(function (tag) { %>
    <li><%= tag.name %></li>
    <% }); %>
</ul>

On the Client Side

Outputting from the server side (controller or view)

<%= isotope_render_template("articles/article", :id => "article-template") %>

The above code will output:

<script type="text/x-isotope" id="article-template">
<h2><%= item.title %></h2>
<div class="content">
<%= item.content %>
</div>
<ul class="tags">
    <% item.tags.forEach(function (tag) { %>
    <li><%= tag.name %></li>
    <% }); %>
</ul>
</script>

which is easy to evaluate with any JS object using the mentioned technique.

On the Server Side (The Holy Grail)

Using Johnson, the famous micro-templating technique and JSONed Ruby objects, this library provides the following functionality:

<%= isotope_render_partial("articles/article", :locals => { :item => @article }) %>

This code reads the source of the EJS file, uses Johnson and John Resig’s technique and serves a string as an output.

Configuration

External JS files can be included in order to have special functionality in the EJS templates, on both client and server.
To include files, create a config/isotope.yml file and fill this array, relatively to the app root:

include_scripts:
    - /public/javascripts/isotope_functions.js

And in order to have it available on client side, put this line before the <body> close:

<%= isotope_included_scripts.html_safe %>

Scripts will be automatically included in server side.

Scripts and templates are never cached on development environment.

Installation:

# Rails 3.x
ruby script/rails plugin install git@github.com:elado/isotope.git
# Rails 2.3.x
ruby script/plugin install git@github.com:elado/isotope.git
Rails
Rails 3.x

Add to your Gemfile

gem 'json'
gem 'johnson'
gem 'isotope'

and run bundle install

Rails 2.3.x

Add to config/environment.rb

config.gem 'json'
config.gem 'johnson'
config.gem 'isotope'

and run rake gems:install

Add to config/environment.rb

require ‘isotope’

Server Side Example:
# ArticlesController
def show
    @article = {
        :title => "Hello!",
        :content => "World!",
        :tags => [
            {:name => "tag 1"},
            {:name => "tag 2"},
            {:name => "tag 3"},
            {:name => "tag 4"}
        ]
    } # Or an ActiveRecord fetch
    render :text => isotope_render_partial('articles/article', :locals => { :item => @article })
end

Or, with a view:

# ArticlesController
def show
    @article = {
        :title => "Hello!",
        :content => "World!",
        :tags => [
            {:name => "tag 1"},
            {:name => "tag 2"},
            {:name => "tag 3"},
            {:name => "tag 4"}
        ]
    } # Or an ActiveRecord fetch
end
# views/articles/show.html.erb
<%= isotope_render_partial('articles/article', :locals => { :item => @article }) %>
Client Side Example:
# views/articles/show.html.erb
<%= isotope_render_template('articles/article', :id => "article") %>
Sinatra

Actually the same usage, more or less.

Run Tests

Install rspec (gem install rspec)

rspec test/isotope_spec.rb

Launch sample Rails app

cd examples/rails3-example && rails s

and go to http://localhost:3000

Columns With Separator Border in CSS: The Easy Way

Monday, July 12th, 2010

CSS comes with quite a few production faults. You can’t properly vertically align, and you can’t comfortably create columns in a website without hacks.

Creating two columns with a dividing border, with the border’s length depending on that of the longest column and without the use of background images or JavaScript, is also a non-standard task, because you can’t affect the height of one element based on the height of another.

In the image on the right there are two floated divs used to create columns. If there’s a border on the left column and shorter than the right column, the border will also be shorter.

The left column’s border reaches the bottom

The left column’s border reaches the bottom

The left column’s border reaches the end of the shorter column

The left column’s border reaches the end of the shorter column

The Required Result

The border continues along the longer column

The border continues along the longer column

The secret

Both columns have a border. The left column is displaced one pixel to the right. This way, the borders overlap, and the longest border reaches the bottom of the page.

Each column has its own border, but the left column is moved one pixel right, overlapping the right column

Each column has its own border, but the left column is moved one pixel right, overlapping the right column

In general, the concept of moving element with negative margins or position is quite interesting and can be an effective solution to many problems. I’m sure I’ll deal with it in future posts.

Code

<style type="text/css">
#menu {
	float:left;
	border-right:1px solid #000;
	width:150px;
	padding:10px;
	background-color:#6E919A;
}
#content {
	float:left;
	border-left:1px solid #000;
	width:250px;
	margin-left:-1px;
	padding:10px;
	background-color:#9BC9D1;
}
</style>
<div id="menu">Menu<br/>Menu<br/>Menu<br/>Menu</div>
<div id="content">Content<br/>Content</div>

OhBehave – Apply Behavior to Static/Dynamic Elements, Immediately

Wednesday, June 23rd, 2010

Applying a behavior to an element is a very common task in web applications and rich web sites. Something like an element onload event that is called whenever it’s ready, regardless of whether it’s pre-created in the html, or dynamically added.

For example:

  • Converting a dropdown list (<select> tag) to an AutoComplete/custom designed select control
  • Converting input type=checkbox/radio into images
  • Wrapping an element with a complex frame (rounded corners, shadows, opacity)
  • A floating label on a textbox and hiding it on a value change or focus

The Common, Bad Solution

A standard solution will be waiting until the DOM is ready (a function that exists in every self-respecting JS library), finding all elements with a certain class and calling a JS function on that element that will attach the behavior.

Disadvantages of this solution:

  • Sometimes, it takes a little while until the DOM is ready, and meanwhile, the element appears in its original, behavior-less form
  • When elements are dynamically added to the DOM (innerHTML / createElement & appendChild) – the behavior must be applied again on all new elements, manually.
  • A generic solution for dynamic elements is having an interval in which looks for these elements. However, this lookup is slow and the delay may cause flickering.

Code Sample for the Bad Solution:

function applyBehaviors() {
    var elements=$.select(".behavior");
    for (var i=0;i<elements.length;i++) applySpecificBehavior(elements[i]);
}
$.domready(function () {
    applyBehaviors();
});
$.ajax("url.html",{
    onSuccess:function () {
        applyBehaviors();
    }
});

Or, an interval:

setInterval(function () {
    var elements=$.select(".behavior");
    for (var i=0;i<elements.length;i++) {
        if (elements[i]._behaviorApplied) continue;
        applySpecificBehavior(elements[i]);
        elements[i]._behaviorApplied=true;
    }
},200);

(The functions $.select, $.domready and $.ajax can be found in any JS library).

The Desired Solution – OhBehave

OhBehave is a neat script that is responsible for  applying behaviors to elements immediately, as soon as they are available, without waiting for the rest of the DOM to load. In addition, it also applies the behavior on new dynamic elements.

You can create new elements on the fly or set an element’s innerHTML with new html tags, without worrying about attaching behaviors. An <input type=”checkbox”> can be converted to image base on its .checked property as soon as it’s appended to the document.

If you use Ajax to get HTML from server, you shouldn’t be worried about wiring up events to elements.

As Event Delegation is good for attaching events to all elements of a certain type, OhBehave does the same for more complex behaviors.

How does it work?

For every browser I had to find a different solution, as each of which implements the desired functionality differently. Each of the implementations is a small browser-specific code that calls OhBehave.initialize, which receives an element and applies all behaviors needed.

The behaviors for that element are defined in its class attribute, each of which is prefixed with “oh-behave-” (optional). E.g. oh-behave-AutoComplete, oh-behave-WrapWithFrame.

Example for a behavior

OhBehave.behaviors["AlertTheTime"]=function (element) {
    element.onclick=function () {
        alert(new Date());
    };
};
// or, as a global function -- function AlertTheTime(element) { ... }

Implementations

Firefox (Gecko Engine) – XBL

XBL, or – XML Binding Language, is a markup language, based on XML, that is defined according to a W3C standard and is used for applying rich behavior to an element. XBL allows us to declare properties (including getters/setters), custom events (and to fire them), custom methods and custom style. The XBL is placed on an element using css:

.oh-behave { -moz-binding:url("oh-behave.xml#oh-behave"); }

The XML structure:

<bindings xmlns="http://www.mozilla.org/xbl" xmlns:html="http://www.w3.org/1999/xhtml">
<binding id="oh-behave">
        <implementation>
            <constructor>OhBehave.initialize(this);</constructor>
        </implementation>
    </binding>
</bindings>

As for now, only the Gecko engine supports XBL, even though it’s a standard.

IE (Trident Engine) – HTC

HTC, or – HTML Component is Microsoft’s implementation for applying rich behavior to an element. It supports more or less the same as XBL.

Safari/Chrome (WebKit Engine) and Opera (Presto Engine) – The Event DOMNodeInserted

As WebKit/Presto didn’t include XBL yet (there are some mentions in the source code but it’s still turned off probably), I had to find a different solution.

The DOMNodeInserted event (a W3C standard) is fired any time an element is inserted dynamically (innerHTML / createElement & appendChild). Through this event I look for elements that should have the behavior. The lookup is done with querySelectorAll – a method of a document or element that gets a CSS selector and returns all elements for that selector.

For elements that are already in the document, I used the DOMContentLoaded event, which may cause a small flick but the engine is so fast that I barely believe it’s noticeable.

Code

// behavior example - assigns alert of the current time onclick
OhBehave.behaviors["AlertTheTime"]=function (element) {
	element.onclick=function () {
		alert(new Date());
	};
};
// behavior example - adds a frame around an element
OhBehave.behaviors["WrapWithFrame"]=function (element) {
	var frame=document.createElement("div");
	frame.className="cornerized";
	element.parentNode.replaceChild(frame,element);
	frame.appendChild(element);
	var corners=["tl","tr","bl","br"];
	for (var i=corners.length;i--;) {
		var corner=document.createElement("div");
		corner.className="c "+corners[i];
		frame.appendChild(corner);
	}
};
<div class="oh-behave oh-behave-WrapWithFrame oh-behave-AlertTheTime">Auto apply behavior on existing element</div>

When the div appears on the screen, it obtains immediately the onclick event and being wrapped with a box with 4 corners.

Demo

In the demo page there are two behaviors applied on same element: 1) Wraps the element with a frame and some other elements, 2) Attaches a click event to show current date.

In addition, there’s a button that adds more elements, dynamically and the behaviors applied automagically.

Demo of OhBehave | Download Source

Attention! In case you only need to attach events, you should use the Event Delegation method.

Table Layout in CSS

Wednesday, June 23rd, 2010

I’ve been in this situation quite often:
We have a list of elements that need to be laid out on the page in the following manner:

  • Each element has fixed width / height.
  • There are a number of elements in a row.
  • There must be an equal space between any two elements.
Even spaces between each cell

Even spaces between each cell

Well, this is a classic case for a table! Yes! I can see it happening; with the right cellspacing and background color, it will be perfect.

NOT.

So, CSS.
The requirements are:

  • Each item is 50×50 pixels.
  • 15 pixels between items.
  • 4 items in a row.

We’ll create a <ul> to contain all the items. Its width will be 245 pixels, to house 4 items and 3 spaces between them (4*50 + 3*15).
To create a list of items in a row we shall use float: left (or right) in order to place the elements on a particular side. Every time an item reaches the edge of the box in which it is contained, it will drop down to the next row.
To create the spaces between the items, we’ll use margin-right. And here is the catch:

The white spaces are the margins on items that are supposed to be in the first row

The white spaces are the margins on items that are supposed to be in the first row

As you can see, the spacing is created for each item.

Due to a lack of spacing on the right, the last block of each row drops down. Because of the bottom margins, there is superfluous space at the bottom of the container. Our design has gone wrong.

How about if we don’t want the last column to have a side margin and the last row a bottom margin?

The sophistication of CSS3 (nth-child) is yet to be here.

The Trick

We keep the margins.

We’ll enlarge the container <ul> to 260 pixels, so that it contains the right margin of the last box.

We create another wrapper div, hide the overflow and give it a width of 245 pixels, and inside we place the <ul> with our items. The <ul> with the items will actually flow on, but because we’ve “trimmed” it the new wrapper div, we won’t see the superfluous space on the side.

And what about the space at the bottom? We’ll give the <ul> containing the items a negative margin-bottom as large as the items’ lower margin.

Code

<style type="text/css">
.item-list-wrapper {
	overflow:hidden;
	width:245px;
	border:5px solid #000;
	font-family:verdana;
}
	.item-list,.item-list>li { margin:0; padding:0; list-style:none; }
	.item-list {
		overflow:hidden;
		width:260px;
		margin-bottom:-15px;
	}
		.item-list li {
			background-color:#ADCBDA;
			width:50px;
			height:50px;
			margin-right:15px;
			margin-bottom:15px;
			float:left;
		}
</style>
<div class="item-list-wrapper">
	<ul class="item-list">
		<li>item</li>
		<li>item</li>
		<li>item</li>
		<li>item</li>
		<li>item</li>
		<li>item</li>
		<li>item</li>
		<li>item</li>
		<li>item</li>
		<li>item</li>
		<li>item</li>
		<li>item</li>
	</ul>
</div>

This example can contain as many <li>’s as you want, without harming the layout.

Table Layout in CSS – Example page