Attack of the Clones

Comments Off on Attack of the Clones

Cloning Nodes is Legal!

ChocolateChip provides several methods for dealing with nodes. These are:

Element.clone()
Makes a copy of the element.
Element.empty()
Deletes all of an element’s child nodes.
Element.remove()
Deletes an element.
Element.wrap()
Wraps an element with the supplied markup.
Element.unwrap()
Removes the markup containing an element, leaving the element in the document.
$.replace()
Replaces one element with another.

 

Element.clone()

The Element.clone() method creates a copy of an element. If a boolean value that evaluates to true is passed, the element will be copied along with all of its child nodes. And if a false value is passed in, the method copies only the element, ignoring its child nodes.

<p>This is a paragraph.</p>
$.ready(function() {
    // This will clone the node with its child node, which is a text node.
    var p1 = $("p").clone();
    // This will output "This is a paragraph"
    console.log(p1.text());
    // -1 will evaluate as a false boolean.
    // This will result in only the barebones p tag being cloned, not its content.
    var p2 = $("p").clone(-1);
    // This will not output anything since p2 has no child nodes.
    console.log(p2.text());
});

Element.empty()

The Element.empty() method will delete all child nodes from the element

<p>This is a paragraph.</p>
$.ready(function() {
    // This will remove the text from the above paragraph.
    $("p").empty();
});

Element.remove()

This method completely deletes a node from the document. Before deleting the element, it sets any events on the element to null. If you know what events you have bound on that element, you should probably use the Element.unbind() method to remove them before removing the element. That way your events are completely eradicated.

<p>This is a paragraph.</p>
$.ready(function() {
    // Delete the above paragraph from the document:
    $("p").remove();
});

Element.wrap()

The Element.wrap() method wraps the element in the supplied markup. This is similar to the outerHTML method, except that it outputs true DOM nodes. In order to accomplish this, the method clones the node along with all of its children. It then creates the new markup to wrap around the node and inserts the node inside of the new markup. Then it replaces the node in the document with this newly wrapped node. This means that if you had bound and events to the original node, you will need to reattach them after wrapping. If you intend to do a lot of wrapping/unwrapping, and maintain event binding, then you should use event delegation by bind the events to the parent of the elements you intend to wrap or unwrap and capture the events during the bubbling phase. See the entry “Handing Events” for examples of this technique.

<p>This is a paragraph.</p>
$.ready(function() {
    // This will wrap the above paragraph in this div tag:
    $("p").wrap("<div id="paragraphWrapper"></div>");
});

Element.unwrap()

The Element.unwrap() method removes the parent element from an element. To do this, it clones the node, then replaces the parent node with the cloned node. As with the Element.wrap() method, any bound events will be removed, but you can use event delegation to avoid this problem.

<div id="paragraphWrapper"><p>This is a paragraph.</p></div>
$.ready(function() {
    // This will remove the div surrounding the above paragraph:
    $("p").unwrap("");
});

$.replace()

The $.replace() method replaces one element with another. The first argument is the element you want to replace with. The second argument is the element you want to replace. As with the Element.wrap() and unwrap methods, any bound events will be removed, but you can use event delegation to avoid this problem.

<p>This is a paragraph.</p>
$.ready(function() {
    var divTag = $.make("<div id="replacementNode">A new div here.</div>");
    // This will replace the p tag with the newly created div tag:
    $.replace(divTag, $("p"));
});

Ajax and JSON

Comments Off on Ajax and JSON

ChocolateChip provides method for AJAX updates and JSON data binding.

Element.xhr()
A method to perform basic HttpRequests
Element.xhrjson()
A method to perform JSON data binding.

 

Element.xhr()

The Element.xhr() method provides a way to update parts of a document with dynamic content pulled from another external document. This would normally involve document fragments, but could also be a complete document, depending on what your needs are. The If no parameters are passed, the Element.xhr() method replaces the content of the target element with the content of the requested document.

<p id="update">Old text here.</p>
$(function() {
    // This will replace the content of the paragraph above with the content of the requested document.
    $("#update").xhr("../data/newUpdate.html");
});

The Element.xhr() method can also receive and object literal of options to execute depending on the success of the HttpRequest. These are successCallback and errorCallback. The successCallback allows you to do something after the successful insertion of the new content. The errorCallback allows you to do something when HttpRequest fails. You can provide both, either one or none. Here’s an example of how to provide for both:

<p id="update">Old text here.</p>
<p id="update-message"></p>
$(function() {
    /* This will replace the content of the paragraph above with the content of the requested document. If the script is successful, it will execute the successCallback, and if it is not, it will execute the errorCallback.
*/
    $("#update").xhr("../data/newUpdate.html", {
        successCallback: function() {
            $("update-message").text("The update was successful!");
        },
        errorCallback: function() {
            $("update-message").text("There was a problem retrieving the requested file.");
        }
    });
});

Element.xhrjson()

The Element.xhrjson() method provides a way to do rudimentary data binding with JSON. You do this by passing in the URI for the JSON document you want to bind to, followed by an object literal that defines which elements of your document get mapped to which values of the JSON object. The JSON document can be created and updated dynamically on the server. That way, each time your document requests it, the bound elements in your document will be updated with the current data. It’s a good idea to have some default values in your document in case there is a problem retrieving the JSON fie.

In the example below, we are binding JSON values to the corresponding element ids in a document:

Here is a json data file named data.html:

{
	'firstName': 'Robert',
	'lastName': 'Biggs',
	'role': ' and the author of ChocolateChip', 
	'job': ' front-end developer'
}

Here is a section of HTML with some default values that we wish to replace with the values from the json file:


<div id="content"><span id='firstName'>Joe</span> <span id='lastName'>Bodoni</span> is a <span id='job'>great waiter at a restaurant downtown</span></div>

And here’s the JavaScript to get the JSON file using $.xhrjson():

$(function() {
    $("#content").xhrjson("data.html", {
            "firstName": "#firstName",
            "lastName": "#lastName",
            "role":"#role",
            "job": "#job"
	});
});

Update Nov 19, 2010

As of version 1.0.7, ChocolateChip has a $.template() method for allowing quick and simple JavaScript-based templates on the client side. This is basically a repurposing of John Resig’s (http://ejohn.org) JavaScript Micro Template framework. It’s a very small amount of code that provides the ability to create complex templates with ease.

Here’s an example of a typical template:

<script id="template_1" type="text/jsmt">
	<div>
		<% for (var i = 0; i < users.length; i++) { %>
			  <p>Name: <strong><%= users[i].fname %></strong> <em><%= users[i].lname %></em></p>
		<%  } %>
	</div>
</script>
<div id="template_1_output"></div>

First thing, notice that the JavaScript template is inserted where it the data will be output. This is not a requirement. The template can be anywhere in the document. It doesn’t matter. What matters is that the script have a unique ID so that you can get it through a DOM query and that it have a special MIME type, which is “text/jsmt.” Jsmt stands for JavaScript Micro Template, obviously there is no browser that knows what a script of type jsmt is, so browsers don’t know how to parse and display the contents of the script tag. That means none of the html will be rendered. This is not a bug in browsers, this is how they are supposed to function. If you look at the contents of the script tag you will see we have some HTML tags for defining the template’s structure and we have JavaScript enclosed in and tags. The first set, is for normal JavaScript code. The second set of tags, is used for processing a variable in the template. If you’ve used ASP or JSP in the past, you will recognize these tags.

After the $.template method processes the script tag, it outputs the result. It’s your job to decide where you want to output the result to. You can store it in a variable and out put it later, or immediately. So, from our previous example, to process the template, we use the $.template() method:

   // Process the template of ID "template_1" with JSON data in the variable "data".
    var r = $.template("template_1", data);
    // insert the results of the parsed template into the div with id "template_1_output."
    $("#template_1_output").insert(r);

We could also perform conditional checks inside the tags before outputting the data:

<script type="text/jsmt" id="jsmt_list_items">
	<% people.each(function(name) {
                // Only output names with a y in them:
		if (/y/.test(name)) { %>
		<li><%= name %></li>
	 <% }
	}); %>
</script>

Update Dec 23, 2010

As of version 1.0.8, ChocolateChip offers two methods to enable the HTML5 dataset API for browser versions that support it. For those that do not it has a fallback using normal DOM get and set attribute methods.

Element.data()
A method add or retrieve dataset values
Element.removeData()
A method to remove a dataset from and element.

According to the dataset API in HTML5, any element can have datasets indicating information relative to that element. a dataset value always begins with “data-“. Here’s and example of an element with datasets:

<ul>
   <li data-first-name="John" data-last-name="Doe" data-occupation="teacher">John Doe<li>
   <li data-first-name="Sam" data-last-name="Smith" data-occupation="doctor>Sam Smith</li>
</ul>

Using Element.data() we can get any of the dataset values in this way:

console.log("John's occupation is: " + $("li:first-of-type").data("occupation"));

Similarly, we can set a dataset value as follows:

(function() {
   var personnelHeight = [ "5f8n", "5f10n", "6f0n" ];
   var heightnum = 0;
   $$("li").each(function(item) {
      item.data("personnel-height", personnelHeight[heightnum]);
      heightnum++;
   });
})();

To remove a dataset, use the Element.removeData() method:

(function() {
   $$("li").each(funciton(item) {
      item.removeData("first-name"); /* Removes data-first-name from each list item. */
   });
})();

Special Tools for Special Needs

Comments Off on Special Tools for Special Needs

ChocolateChip has several utility methods that enable you to do useful things. These are:

$.extend()
This method can take two arguments: the object to extend and the properties to extend it with. If only properties are passed, they are added directly to the $ object to extend ChocolateChip itself. The properties are passed as an object literal of key/values pairs.
$.importScript()
This method imports the indicated script into the document by attaching it to the head tag. It accepts a valid URI to the script.
$.hideURLbar
If the mobile browser is not in standalone mode, meaning that the user has not yet installed the Web app on a mobile device, this method hides the browser address bar by slightly scrolling the page to force the addressbar out of view. Until the user actually adds the mobile Web app to the mobile device, this method will help make the online browser use of the Web app feel mofe like an installed app.

Using $.extend()

$.extend({
	growl : "Woof!",
	bark : function(msg) {
		if (!msg) {
			msg = this.growl;
		}
		console.log(msg);
	}
});
	
$(function() {
	$.bark();
	$.growl = "Bow wow! Bow wow!";
	$.bark();
	$.bark("Meow!")	
	
	/* This outputs:
	Woof!
	Bow wow! Bow wow!
	Meow!
	*/
});

Using $.importScript()

If you need to dynamically import a script for whatever reason, you can use this method to do so. It inserts the script into the head tag, at which point the browser immediately parses the scripts and executes it. Simply pass in a valid URI for the script:

$(function() {
	$.importScript("../scripts/additional-functions.js");
});

Using $.hideURLbar()

You want to execute the $.hideURLbar() method whenever the user does something that might cause the browser addressbar to remain in view, such as when the user navigates to annother page, or when the user changes the orientation of the device. To hide the addressbar, simply invoke the method after whatever code might cause the scrollbar to appear:

$(function() {
	$("#aboutButton").bind("click", function() {
		/* This code adds a class to the about panel, causing it to slide into view. */
		$("#aboutPanel").toggleClass("current");
		// After scrolling the panel into view, make sure the addressbar is not showing.
		$.hideURLbar();
	});
});

From version 1.0.7, ChocolateChip offers the following four methods: $.delay(), $.defer(), $.enclose(), and $.compose().

$.delay()

This method enables the delayed execution of a function. $.delay() takes two arguments, a function and an integer indicating how long to wait before executing the function:

$.delay(function() {
     console.log("This message is delayed by two seconds.");
}, 2000);

$.defer()

The $.defer() method cause the execution of a function to wait until the callstack is clear. This means that regardless of where this is in a block of code, it will be executed after all other code.

 $.defer(function() { 
     console.log("This will be executed after all the other code."); 
}); 
console.log("This will be executed before the code before it."); 

$.enclose()

This method enables you to enclose a method inside another method so that you can do something before and after the enclosed method’s execution. It takes two arguments, the function to enclose and the function that encloses it. Below you can see that the function “hello” gets passed in the the enclosing anonymous function as the term func, which allows us to envoke it as func(“Stan”) with concatenation before and after it:

var hello = function(name) { return "Hello, " + name + "!"; };
hello = $.enclose(hello, function(func) {
     return "Before I said, \"" + func("Stan") + "\" I thought about it for a while.";
});

$.compose()

This method enables the ability to pass one function as an argument of another, building up a chain of passed function arguments. When multiple functions are passed as arguments, they are passed in as arguments from the right to the left, so that the last argument becomes the argument for the function preceding it, etc.

var greet = function(name) { return "Hi there, " + name; };
var exclaim  = function(statement) { return statement + "!"; };
var remark = function(remark) { return remark + " You know I'm glad to see you."; };
var welcome = $.compose(remark, greet, exclaim);
console.log(welcome('Jeff')); // => Hi there, Jeff! You know I'm glad to see you.

Besides these, there are a couple of others. First off, ChocolateChip automatically outputs a class of “portrait” or “landscape” on the body tag when the mobile device’s orientation changes. You just have to write your CSS to be aware of that. Something like this would work:

body.portrait > article {
	background-color: green;
	color: red;
}
body.landscape > article {
	background-color: red;
	color: green;
}

Customize the aliases for ChocolateChip

In case you are really, really want to use some other library with ChocolateChip, and you’re using one that also uses $ and $$, ChocolateChip will automatically reassign its $ and $$ methods to __$ and __$$ respectively. If you don’t want to use these either, you can assign them to whatever other aliases you wish. To make this all work, though, you’ll need to import the other library before ChocolateChip, otherwise ChocolateChip will clobber the $ and $$ of the other library. To reassign the default ChocolateChip aliases, do so in a $.ready block as follows:

<script src="someOtherLibrary" type="text/javascript"></script>
<script src="chocolatechip-1.0.6.js" type="text/javascript"></script>

<script type="text/javascript">
$(function() {
    window.cc = window.__$;
    window.CC = window.__$$;
    // Now you can use cc and CC for ChocolateChip's $ and $$ while using the $ and $$ of the other library.
});
</script>

IMPORTANT!

If you change the reference to $ and $ and you have any external ChocolateChip scripts that use $, you will need to update those as well or you’ll get an error because $ and $$ will not point to the ChocolateChip versions. Although you can change ChocolateChip’s global alias to whatever you want, ChocolateChip will continue to use $ internally.

Device detection

ChocolateChip also provides a number of device tests so that you can write custom code for different devices. The test return true or false. Here is what’s available:

  1. iphone
  2. ipad
  3. ipod
  4. android
  5. webos
  6. blackberry
  7. online
  8. standalone

Here’s an example of device detection:

if (!$.iphone) {
	alert("This app only works with the iPhone.");
}
if (!$.standalone) {
	alert("Please install this app on your homescreen by pressing the plus icon in the toolbar below.")
}
if (ipad) {
	$.importScript("../scripts/ipad-script.js");
}

Update Dec 23, 2010

As of version 1.0.8, ChocolateChip offers the following two methods:

Element.getTop()
A method get the offset top of an element relative to the document’s top.
Element.getLeft()
A method to get the offset left of an element relative to the documents left.

These two methods return their position as integers without any length identifiers:

var elem = $("#favItem");
var elemPos = "top: " + elem.getTop() + "px; left: " + elem.getLeft() + "px;";
console.log("The element's position is: " + elemPos);

New in version 1.2.0:

Object Extension:

Just as you can use forEach() or each() on an array, ChocolateChip also provides a convenient way to iterate over an object using Object.each(). Like each() on an array, you can access the key and value of the object. As with the array version, you can name these two arguments whatever you want. The first argument will always be the key and the second, the value”

var obj = {"i-1": "item 1", "i-2":"item 2", "i-3":"item 3", "another-one":"item 4", "something-else":"item 5"};
obj.each(function(key, value) {
    $("ol").append("<li>" + value + "</li>");
});

This will output:

<ol>
   <li>item 1</li>
   <li>item 2</li>
   <li>item 3</li>
   <li>item 4</li>
   <li>item 5</li>
</ol>

Effective Use of Selectors

Comments Off on Effective Use of Selectors

A Feast of CSS3 Selectors for You

ChocolateChip uses the DOM’s built-in selector engine to get elements. Because this uses CSS3 selectors, you can use any combination of CSS3 selectors to get an element. This is especially important because a properly crafted CSS3 can help you avoid having to do other filtering tasks later to find the exact element that you wanted. By using a carefully constructed selector, you managed to pluck it out of the document with your first query and are ready to do something with it. Gone are the days of getting every li or p tag and checking them with JavaScript for certain conditions. Instead you can define you selector in such a way that you get a collection of the desired nodes from the start.

The W3C has a list of all the selectors that are available to use. At some point you want to read through the document carefully to fully understand how these selectors work. The barebones list is as follows:

Pattern Meaning Described in section
* any element Universal selector
E an element of type E Type selector
E[foo] an E element with a “foo” attribute Attribute selectors
E[foo=”bar”] an E element whose “foo” attribute value is exactly equal to “bar” Attribute selectors
E[foo~=”bar”] an E element whose “foo” attribute value is a list of
whitespace-separated values, one of which is exactly equal to “bar”
Attribute selectors
E[foo^=”bar”] an E element whose “foo” attribute value begins
exactly with the string “bar”
Attribute selectors
E[foo$=”bar”] an E element whose “foo” attribute value ends exactly
with the string “bar”
Attribute selectors
E[foo*=”bar”] an E element whose “foo” attribute value contains the
substring “bar”
Attribute selectors
E[foo|=”en”] an E element whose “foo” attribute has a
hyphen-separated list of values beginning (from the left) with “en”
Attribute selectors
E:root an E element, root of the document Structural pseudo-classes
E:nth-child(n) an E element, the n-th child of its parent Structural pseudo-classes
E:nth-last-child(n) an E element, the n-th child of its parent, counting
from the last one
Structural pseudo-classes
E:nth-of-type(n) an E element, the n-th sibling of its type Structural pseudo-classes
E:nth-last-of-type(n) an E element, the n-th sibling of its type, counting
from the last one
Structural pseudo-classes
E:first-child an E element, first child of its parent Structural pseudo-classes
E:last-child an E element, last child of its parent Structural pseudo-classes
E:first-of-type an E element, first sibling of its type Structural pseudo-classes
E:last-of-type an E element, last sibling of its type Structural pseudo-classes
E:only-child an E element, only child of its parent Structural pseudo-classes
E:only-of-type an E element, only sibling of its type Structural pseudo-classes
E:empty an E element that has no children (including text
nodes)
Structural pseudo-classes
E:link
E:visited
an E element being the source anchor of a hyperlink of
which the target is not yet visited (:link) or already visited
(:visited)
The link pseudo-classes
E:active
E:hover
E:focus
an E element during certain user actions The user action pseudo-classes and 2
E:target an E element being the target of the referring URI The target pseudo-class
E:lang(fr) an element of type E in language “fr” (the document
language specifies how language is determined)
The :lang() pseudo-class
E:enabled
E:disabled
a user interface element E which is enabled or
disabled
The UI element states pseudo-classes
E:checked a user interface element E which is
checked (for instance a radio-button or checkbox)
The UI element states pseudo-classes
E::first-line the first formatted line of an E element The ::first-line pseudo-element
E::first-letter the first formatted letter of an E element The ::first-letter pseudo-element
E::before generated content before an E element The ::before pseudo-element
E::after generated content after an E element The ::after pseudo-element
E.warning an E element whose class is “warning” (the document
language specifies how class is determined).
Class selectors
E#myid an E element with ID equal to “myid”. ID selectors
E:not(s) an E element that does not match simple selector s Negation pseudo-class
E F an F element descendant of an E element Descendant combinator
E > F an F element child of an E element Child combinator
E + F an F element immediately preceded by an E element Adjacent sibling combinator
E ~ F an F element preceded by an E element General sibling combinator

Out of the above selectors, the attribute selectors and the structural pseudo-classes offer you a whole lot of power in selecting otherwise difficult to define elements. You can get elements according to their attributes or the values of their attributes. You can get elements based on their relation to their siblings. You can even get an element based on whether it’s empty or not or by excluding elements of a certain type.

OK, time for some practical examples. One of the most common visual styling needs is zebra striping of rows or list. With CSS3 selectors this is easy. You have several ways to do this:

li:nth-of-type(even) – all even list items
li:nth-of-type(odd) – all odd list items
li:nth-of-type(10) – the 10th list item

Similarly, to we can use the nth-child selector to get the children of an element using the same notation. Here we’re going to to define a repeating set of four paragraphs. The first selector targets the first paragraph in that set, etc.

section:nth-child(4n+1) – first paragraph out of a set of four
section:nth-child(4n+2) – second paragraph out of a set of four
section:nth-child(4n+3) – third paragraph out of a set of four
section:nth-child(4n+4) – fourth paragraph out of a set of four

To get the 9th child out of a repeating set of 10, you can use this:
section:nth-child(10n-1) – would get 9th, 19th, 29th, 39th, etc.

As I mentioned, you can also get an element based on whether it’s empty, or you can exclude all elements of a particular type.

p:empty – a paragraph with no child nodes
:not(p) – all nodes except those of type “p”
p:not(p:empty) – all p tags except those that are empty
p:not(p[class]) – all p tags that do not have a class attribute.

From the above examples it should be clear the value of creating efficient selectors when you need to access elements in a document. Constructing a proper selector can eliminate the need for post filtering with JavaScript. This in turn will make your app faster and more responsive for the user.

Difference Between :first-child and :first-of-type

<section>
    <p>The first paragraph</p>
    <div>The first div</div>
    <div>The last div</div>
    <p>The last paragraph</p>
</section>

One last thing, you should be aware of the difference between :first-child and :first-of-type. If you look at the above markup you’ll see there are several paragraph tags and divs. You might think that you could use $("div:first-child") to get the first div. You’d be wrong. In the above code the first div is not the first child, it’s the second child. As such you would not get anything. To get the first div you’d need to use $("div:first-of-type). But if you really wanted the first child, regardless of what type of tag it is, you could use $("section").first() or $("section").firstElementChild. The same would apply when getting the last div or the last child in the above example. $("div:last-child") would not return the last div. $("div:last-of-type") would. To get the last child regardless of the tag, you could use $("section").last() or $("section").lastElementChild.

Animation with CSS Transitions

Comments Off on Animation with CSS Transitions

CSS3 Transitions Created Dynamically with JavaScript

For mobile devices CSS3 transitions and key frame animations are the best way to implement animation. They efficient and fast and be implemented with almost no JavaScript. At CSS3Wizardy I’ve been blogging about how to implement various user interactions and controls for mobile Webkit using CSS3. Most of these are triggered by just the addition or removal of a class.

As of version 1.0.7, ChocolateChip offers a way to create dynamic CSS3 transitions. It does this through the Element.anim() method. This takes three parameters: a object literal of CSS3 properties/values to animate, the duration, and the easing. Here’s an example:

$("#animate").bind("click", function() {
    this.anim({"-webkit-transform": "rotate3d(30, 150, 200, 180deg) scale(3) translate3d(-50%, -30%, 140%)", "opacity": .25, "-webkit-transform-style" : "preserve-3d", "-webkit-perspective": 500}, 2, "ease-in-out");
});

If you have animations needs beyond what CSS3 transitions or key frame animations can deliver, you might want to look into the excellent JavaScript framework by Thomas Fuchs: Émile. It’s just fifty lines of code, not more than 1k in size. And it does everything that the big frameworks do for animation. Thomas Fuchs, the author of the famous Script.aculo.us JavaScript animation framework for Prototype, created Émile for speed and small foot print. Be aware that if you are doing this for mobile deployment, trying to animate many things on the screen can tax the mobile device’s process and consume valuable memory, leading to data corruption and freezes or crashes. Animate the least as possible.

The Data Cache

Comments Off on The Data Cache

ChocolateChip provides the data cache as a convenient way to store data in relation to a DOM node. The data cached can be a variable, string, boolean value, array, object or function. The cache can handle any of these types without any need to convert them to string or parse them for reuse. By its very nature, the cache stores values by value. In other words, it stores the value at the time it is cached. If that data changes outside the cache, the value in the cache remains unchanged. The cache uses a node as the key to the data being stored. There are two methods to use to handle the cache:

  • Element.cache()
  • Element.uncache()

To associate data with a node, just execute the cache method on the node and pass the data as the argument:

$.(function() {
    $('#user').cache({name: 'Joe', age: '21', job: 'athelete'});
});

After you have cached data on a node, you can retrieve it by simply executing Element.cache() without any arguments:

$.(function() {
    var data = $('#user').cache(); // returns the previous object
    // or you can access object members in the cache directly:
    var userName = $('#user').cache().name; // returns 'Joe'
    var userJob = $('#user').cache().job; // returns 'athelete'
});

To remove data from a node, you use Element.uncache():

$.(function() {
    $('#user').uncache();
});

Newer Entries