ChocolateChip-UI 3

Comments Off

ChocolateChip-UI has been completely rewritten from scratch to support iOS 7, Android Jelly Bean and Windows Phone 8. This also resulted in a complete rewrite of ChocolateChip.js. As such the information presented here is only valid for ChocolateChip versions 2.x.

To learn more about the new versions of ChocolateChip and ChocolateChip-UI, please visit ChocolateChip-UI.com. There you’ll find full documentation on how the new version ChocolateChip-UI works and documentation for ChocolateChip 3.x. The new version of ChocolateChip is in a new Github repository as part of ChocolateChip-UI: https://github.com/sourcebitsllc/chocolatechip-ui.

Why ChocolateChip?

Comments Off

Because it’s small and tastefully efficient.

There are already so many JavaScript frameworks out there. These are designed especially for desktop Web development by eliminating for the end user the myriad of browser quirks and differences. It’s hard to imagine doing desktop Web development without using one of those libraries There are also a number of frameworks designed for mobile Web development. The funny thing, though, is that they use desktop frameworks with add ons for mobile Web development. After using several of these and noticing how much unnecessary baggage was there because of the desktop heritage, I decided to create a framework that was specifically optimized for mobile Web app development. ChocolateChip has no desktop baggage. It has nothing, zero, zilch, nada in it for IE, Netscape Navigator, nor 1 .x or 2.x versions of desktop browsers. ChocolateChip instead utilizes the features available in the modern mobile browser engines used in iPhones, iPads, iPods and Android devices. We’re talking Webkit here, and that’s a good thing.

You know what I’m talking about, like when you want the first or next sibling element and you get an empty text node, or not, depending on the browser. ChocolateChip uses DOM level 3 methods, objects and properties to make DOM traversal easy. I know, you’re going to ask about the selector engine. We don’t need no stinking selector engine. Waste of code. I feel like laughing when I read that a framework’s selector engine is being updated to work with mobile. I have to repeat this, you don’t need a selector engine. You have something better that’s built in. That means it is faster than any selector engine could ever possibly be. It’s called querySelectorAll. It takes as its argument a valid CSS3 selector. Because ChocolateChip uses the browser’s native querySelectorAll feature, you can get elements in this way:

var firstPara = $("#intro p:first-child");
var para5 = $("section > p:nth-child(5)");
var evenItems = $$("li:nth-child(even)");
var externalLinks = $$("a[href$='.php']");

As you might have noticed, the markup looks like jQuery or Prototype. This is on purpose. ChocolateChip has two way of getting nodes. When you want to get a single node, you use the $() method. This will always return the first instance of the selector supplied. When you want to get a collection of nodes, you use the $$() method. This returns a collection in the form of an array, even if it has only one member. jQuery rolls the functionality of this method into the $(), whereas Prototype and Mootools keep them separate. After a lot of consideration, I decided to keep them separate. I didn’t want to do looping on collections behind the scene. I want you, the developer to know what’s going on with your code. The goal of ChocolateChip is to eliminate as much obfuscation as possible.

jQuery wraps everything up in the jQuery object, requiring that you use only jQuery methods on all returned nodes. ChocolateChip returns either a single node or, in the case of a collection, an array of nodes. This means that you can use normal JavaScript on everything that ChocolateChip returns. So, if you decide that you don’t want to use a ChocolateChip method, you can use whatever JavaScript you want to write and mix it in wherever you want with standard ChocolateChip methods.

Rather than writing methods to implement missing features in browsers, I use the native methods already implemented in Webkit. And, although my goal was to make a framework for mobile Webkit, it also works without modification on desktop versions of Safari, Chrome, Firefox and Opera. And as far as IE goes, you can forget everything before IE9.

But then, that’s the whole point. ChocolateChip was not written for desktop browsers, it was written for mobile browsers. If it happens to work for desktop browsers, fine. I will not, however, add code to ChocolateChip just to make it useable for desktop development. That’s not its purpose. Yet I will be looking to see what I can add to ChocolateChip to enhance its usefulness for mobile Web app development.

Because ChocolateChip’s purpose is for mobile, size is of supreme importance. To restrain it’s size as much as possible, I was forced to limit what I put in it. Due to the memory constraints of mobile Web app development, having a smaller framework means having more memory for the content in your app. Running out of memory will cause the mobile browser to crash. Do you follow what I’m saying? You want that framework as small as possible. And ChocolateChip fits the bill well. It’s just 8k when minified.

But if you are really determined to eat up valuable browser memory by using some other framework that also uses $ and $$, you can customize ChocolateChip to use whatever you want as an alias. If $ and $$ are already in the global name space, ChocolateChip will assign its versions of these to __$ and __$$. This is indicated below:

if ((!window.$) && (!window.$$)) {
	window.$ = $;
	window.$$ = $$;
} else {
	window.__$ = $;
	window.__$$ = $$;
}

In such a case, you can reassign __$ and __$$ to whatever other aliases you want to use. You would do this at the start of you own document inside the $.ready() block:

$.ready(function() {
	window.choco = window.__$;
	window.chocos = window.__$$;
});

This allows you to write choco("p:first-child");.

ChocolateChip is available in three flavors, dark (with full comments), regular (with comments removed) and light (minified). If you’re new to ChocolateChip, by all means download the dark version. Crack it open and read the comments. They explain each method, what arguments they take, what they return, syntax, examples, etc. If you’re in development and you want to be able to troubleshoot exceptions in your browser’s Web inspector, you probably want to use the regular one. And when you’re ready to go into production, you want to switch over to the light version. You can get them all from ChocolateChip’s online GitHub repository.

To see examples of what ChocolateChip can do, visit http://www.css3wizardry.com. That site has numerous posts about how to built out various mobile Web controls that work like native controls on the iPhone, iPad, iPod and Android platforms.

Getting Stuff with $() and $$()

Comments Off

Before you do anything, you need to find out if the browser is ready for you

Whenever you want to write some code with ChocolateChip, you need to check to see if the browser has finished parsing the document so that you can get nodes. ChocolateChip makes this possible with the $.ready() method. Here is an example of how to wrap your code in the $.ready function or $(function() {}):

$.ready(function() {
    // Your code goes here.
});

Whatever code you put inside the ChocolateChip ready function will not execute until the document is ready for it. It’s just like jQuery’s $(document).ready(function() {}), or any other framework’s DOMready method. Except that you don’t have to pass in the document as an argument. $.ready() straightaway checks to see when the document is ready. What else would you be waiting for?

So, $.ready() takes one argument, a function to execute when the document is ready. To get your script to execute, you can pass in a predefined function that you’ve defined elsewhere:

var doSomething = function() {
    console.log("This is doing something. Cool, huh?");
};
$.ready(function() {
    doSomething();
});

This works fine. However, in most cases you’ll want to write code blocks inside an anonymous function that is defined directly within the parentheses of the $.ready() function. This isolates your code inside the $.ready() function to avoid namespace collisions with any other code you might have elsewhere:

$(function() {
    var logSomething = function(msg) {
        console.log(msg);
    };
    logSomething("I hope this is working properly.");
});

You can also access the window and document objects with $, and if you pass no selector to $, it will by default return the document object. Please note that although you can access the window and document objects, they do not have the same interface as DOM nodes and therefore most of those methods will not work on the window and document objects.

var windowHeight = $(window).outerHeight; // returns the height of the window frame
var docSource = $(document).title // returns the title of the document
var docSource = $().title // same as above.

All ghosts of selector engines be gone!

Selector engines are now so out of fashion. Why does anyone seriously think they need one when we have querySelector and querySelectorAll? These both take as their argument a valid CSS3 selector. You can’t get any simpler than that. It’s built into the mobile browser too. It’s a complete no-brainer, right?

ChocolateChip uses querySelector and querySelectorAll to return matching nodes in your document. To get a single node is really easy. Just invoke the $() method.

$(function() {
    var elem = $("#menu > li:last-of-type > a");
    elem.css("border-bottom: solid 2px blue");
});

No rocket science here. Just describe what element you want using CSS3 selectors and, bang! You’ve got it and can do stuff. Here I’m using ChocolateChip’s css() method to add inline CSS on the anchor tag in the last list item in a container with an id of “menu.” Bare in mind the the $() method uses querySelector. This means it always returns a single node. If there are multiple matches, $() will return the first match. However, with the power of CSS3 selectors you can craft a selector to pick out the node you want with surgical precision without having to loop through a collection.

OK, now let’s get a little complicated here. If I use $$() to search a document for multiple matches, I get back what’s called an HTML collection. The $$() method turns that collection into an array so that we can do something practical with it:

$(function() {
    $$("#list > li").each(function(item) {
        item.css("background-color: red");
    });
});

So, to clarify what’s going on, when ChocolateChip finds a number of matches for a query, it takes that HTML collection and turns it into an array. Since it returns an array, you can use the forEach method. As a matter of fact you can use any of the array methods you want. In most cases, though, you’ll want to use the forEach method. This takes as its argument a function to execute on each index of the array. This function can take an argument of an object using any name you want to indicate the value of “this” during execution of the function. During the execution of the function, “this” will refer to that particular index of the array. In the above example of code, I get an array of list items. To operate on each list item I use the term “item” as the argument of the method being executed by forEach. You can use whatever term makes sense for the individual members of the array. The nice thing about this is that it makes your code more readable, and that makes your code easier to maintain and easier for other people to use as well.

Danger! Will Robinson, danger!
I could have rolled the two selector methods together. Actually, at one point I did. But that led to several problems. It was crazy trying to decide when to return a single node and when to return a collection. And how to know if the user intended to get a single node or a collection. Rather than writing a mess of code to deal with all the edge cases, I decided that for simplicity and sanity I would keep the two method separate. jQuery handles this problem eloquently by wrapping everything in the jQuery object, where everything is hidden from the user. I did not want to go this route. I wanted everything to stay accessible JavaScript and no obfuscation.

Personally, I want to have a way to get a single node and deal with it as such, not as an object. I want to be able to get a node that I can use normal JavaScript on. I do not want a node wrapped in an object that forces me to use the framework’s methods instead of natural JavaScript methods. I thought long and hard about this and decided that it was better not to use object wrappers.

Besides, there was another reason I didn’t want to hide the looping from you. I wanted to give you the opportunity to choose how you operate on the array returned by $$(). Instead of using forEach(), you might want to use reverse(), sort(), indexOf(), lastIndexOf(), some(), every(), map() or filter(). Depending on your needs, these are all possible array methods that you could use on the array returned by $$(). If you’re a developer of any standing, you’re probably already familiar with these array methods from other programming languages. If you’re not familiar with them, you can read more about them here.

One more thing, don’t forget that if you want, you can use the DOM’s built-in querySelectors. Here’s an example:

// The querySelector method will return a single node:
var elem = document.querySelector("#menuList > li:first-of-type");
elem.style.backgroundColor = "red";

// The querySelectorAll method will return a nodeList, even if only one node is found:
var items = document.querySelectorAll("#menuList > li");
for (var i = 0, i < items.length; i++) {
    items[i].style.cssText = "font: bold 14px/20px Helvetica, Sans-serif;"
}

To see examples of what ChocolateChip can do, visit http://www.css3wizardry.com. That site has numerous posts about how to built out various mobile Web controls that work like native controls on the iPhone, iPad, iPod and Android platforms.

Find and FindAll

As of version 1.1.7 ChocolateChip offers element.find() and element.findAll() as ways to get nodes starting from a particular node. If you already have a node and one to get one or more of its descendants, you can use these methods. Element.find() is works like $() in that it returns a single node, and element.findAll() works like $$() in that it will return a collection.

Here’s an example:

var menu = $("#menu");
var firstMenuItem = menu.find("li");
menu.findAll("li").each(item) {
    item.css("background-color: yellow; border: solid 2px red;");
});

Getting in Touch with Your App

Comments Off

If you click me again, I’m going to get touchy!

So, you can tell when the document is ready for manipulation and you can get nodes in the document. You can probably use normal JavaScript to do interesting things with those nodes. But you’re missing one thing that people need in order to use your app. You need interaction. In other words, you need event handling. You need to know when the user interacts with your app. If you’re using ChocolateChip to develop a mobile Web app, you need to get a finger on touch events. Clicks are for people who have mice, or hamsters, or gerbils, or whatever. People with mobile devices are smearing their greasy fingers all over those devices. You want to be able to capture those smears and do something with them.

ChocolateChip provides two very simple ways to deal with events: bind and unbind. Bind is not a cross-browser workaround that tries to play nice with the browser from that company in Redmond. Bind is using the browser’s standards-based event listener. Because of this, you can bind multiple events of the same type with different blocks of code to execute and they will all fire, unlike the inline event handers. But enough with the explanations. Let’s look at some examples. We’re going to start with some markup which has buttons for navigation, etc. One thing, because you can bind events to any element, I see no reason to use link tags to create buttons. I just hate having to do the return false thing to cancel the href attribute on a link. You can use a div or a span to make your button, and you don’t have to worry about canceling the href attribute or getting rid of the link’s default text underline on hover. The whole idea of ChocolateChip is how to get by with less. With that in mind, here’s my minimalistic markup for a navigation bar:

<article>
	<section>
		<header>
			<span class="button goHome">Back</span>
			<h1>Interface</h1>
			<span class="button about">About</span>
		</header>
		<nav>
			<ul class="list drilldown">
				<li rel="#RadioButtons">Radio Buttons</li>
				<li rel="#ProgressBar">Progress Bar</li>
				<li rel="#Tabs">Tabs with a Popup</li>
				<li rel="#Popup">Popup Window</li>
			</ul>
		</nav>
	</section>
</article>	

What we’re going to do with the buttons is just show how to bind touch events to handle touch interaction visually. In desktop browsers’ when the user’s mouse is over something with a hover state, the user is presented with a visual cue. Mobile devices have no mice, but your fat finger. Mobile devices do not have hover states, but using touch events we can indicate to the user that they’ve touched something by giving it a hover class. And when the touch ends, we remove the hover class. This gives the user the sense that he or she just interacted with your interface. Here’s the code to make that happen with the markup above:

$$(".button").each(function(button) {
    button.bind("touchstart", function() {
        this.addClass("hover");
    });
    button.bind("touchend", function() {
        this.removeClass("hover");
    });
})

Notice that we only need to get that collection of buttons once. Once we have it we can bind events for touchstart and touchend. Touchstart is like mousedown in a desktop browser, and touchend is like mouseup. When the user touches, we capture the event and invoke the ChocolateChip method addClass() to add a class of “hover” to the button. Similarly, when the user ends the touch, we remove the class hover using the ChococlateChip removeClass() method. In the above event handlers, “this” refers to the element being interacted with. Instead of adding and removing classes, you could write some other JavaScript code to do something to the objects referred to by “this.” The this keyword is the real Javascript keyword, not a framework specific reference wrapped in a framework object.

We could similarly add touch events to the list items in our markup. Here is the updated code:

$$(".button").each(function(button) {
    button.bind("touchstart", function() {
        this.addClass("hover");
    });
    button.bind("touchend", function() {
        this.removeClass("hover");
    });
});
$$(".list li").each(function(item) {
    item.bind("touchstart", function() {
        this.addClass("hover");
    });
    item.bind("touchend", function() {
        this.removeClass("hover");
    });
});	

We could also add some navigation to the list items by extending our touch event. We just need to get the rel attribute from each list item and direct the window location to it:

$$(".button").each(function(button) {
    button.bind("touchstart", function() {
        this.addClass("hover");
    });
    button.bind("touchend", function() {
        this.removeClass("hover");
    });
});
$$(".list li").each(function(item) {
    item.bind("touchstart", function() {
        this.addClass("hover");
        window.location = $(this.getAttribute("rel"));
    });
    item.bind("touchend", function() {
        this.removeClass("hover");
    });
});	

As you can see, with a few lines of code, ChocolateChip enables us to accomplish quite a lot. There’s no framework obfuscation going on. It’s human-readable as well as easy to maintain and extend.

Now if you’re a good developer, you know that when you’re done with doing things you need to remove your event handlers. You can do this with the unbind method. The unbind method is attached to the element upon which it fires, just like the bind event. Like the bind event, it accepts an event. It also needs the name of the function or method that the event was executing. Am I getting any raised eyebrows out there yet? OK, let me explain. If the function has no name, you can’t remove the event. What do all the above examples have in common? We’re binding events with anonymous functions. They’re called anonymous because they don’t have names. So, like I said, they can’t be unbound. If your app only has a few bound events, this probably won’t be a problem. If you have a lot of events, this will be a problem, especially if you adding and remove nodes with events attached to them. Not removing the events causes memory leaks, and these over time lead to the browser running out of memory, slowing down and eventually crashing.

To avoid this you have two choices, you can name your anonymous functions or you can do an event reset. One of the things developers love about anonymous functions is that you define them right where you’re executing them. Naming an anonymous function doesn’t mean you have to give up this freedom. Here’s the above code rewritten to use named functions instead of anonymous functions.

$$(".button").each(function(button) {
    button.bind("touchstart", function touched() {
        this.addClass("hover");
    });
    button.bind("touchend", function untouched() {
        this.removeClass("hover");
    });
});
$$(".list li").each(function(item) {
    item.bind("touchstart", touchedItem = function() {
        this.addClass("hover");
        window.location = $(this.getAttribute("rel"));
    });
    item.bind("touchend", untouchedItem = function() {
        this.removeClass("hover");
    });
});	

In the first each loop we bind the elements with named functions: touched() and untouched(). In the second each loop we set the anonymous functions to touchedItems and untouchedItems. Be aware that these functions now reside in the global space. If you wish to avoid polluting the global space with these functions, you can use a pattern of delegated events using event bubbling to capture the events at a higher level in the document. See the section on events for more details about this method of event capture.

Note:

One last thing, if you want to you can also add bindings for click events if you want to test your app in a desktop browser or if you want to make your Web app usable on modern desktop browsers as well.

If you are using ChocolateChip 1.0.7 or later you can also use event delegation as a way to track touch events.

Using $.ready() and $.loadEvent() to execute code

Comments Off

ChocolateChip provides two ways of executing code: $.read() and $(function() {}). These are identical, in that they both execute code as soon as the browser has finished parsing the DOM but before it has visually rendered it to the screen. The advantage here is that you can do things with the document, such as hiding or showing or adding things, before it displays to the user. There is also the $.loadEvent() method waits until the document is completed loaded and output to the screen. This means the user first sees the document load, then sees whatever changes your code makes to the document. In most cases, doing document setup after page load is a bad experience for the end user because they have to wait twice, first for the page to load fully, then for you code to finish executing.

There is also a big difference in how these two methods handle code. In most cases you would pass in your code to $.ready() using an anonymous fuction like this:

$.ready(function() {
	$("#Home").addClass("current");
	$(".button").forEach(function(button) {
	    button.bind("touchstart", function() {
	    	button.addClass("touched");
	    });
	    button.bind("touchend", function() {
	    	button.removeClass("touched");
	    });
	});
});

We could just as well have defined our code outside the $.ready() block and called the functions inside it like this:


var setHomeCurrent = function() {
	$("#Home").addClass("current");
};
var setupButtons = function() {
	$(".button").forEach(function(button) {
		button.bind("touchstart", function() {
			button.addClass("touched");
		});
		button.bind("touchend", function() {
			button.removeClass("touched");
		});
	});
}:
	
$.ready(function() {
    setHomeCurrent();
    setupButtons();
});

The above block works, however, I strongly recomend you put as much code inside the $.ready() block in order to reduce namespace collisions. Putting all your code in the $.ready() block also makes it easier for you so find the code being executing.

The $.loadEvent is excatly like $.ready() or $(function() {}). The only difference is when the code gets executed. As I mentioned above, $.ready() executes code when the browser has finished parsing the document’s nodes. $.loadEvent executes code after the document and all of its resources have been full loaded and rendered to the screen. You can pass in functions to execute, or you can use and anonymous fuction block like $.ready() does to define all your code there. The $.loadEvent() chain loads your functions on to the regular window.onload event. By default you can only pass one fuction to the onload event. $.laodEvent gets around this by putting all the functions into an array and then executing the functions in the array sequentially. If you have several $.loadEvent() declarations in your code, the browser will execute them in the order in which they reside in the code being parsed. Here’s an example of the two ways to use $.loadEvent():


var setHomeCurrent = function() {
	$("#Home").addClass("current");
};
var setupButtons = function() {
	$(".button").forEach(function(button) {
		button.bind("touchstart", function() {
			button.addClass("touched");
		});
		button.bind("touchend", function() {
			button.removeClass("touched");
		});
	});
}:
	
$.loadEvent(function() {
    setHomeCurrent();
    setupButtons();
});

// Or you could code it this way:

$.loadEvent(function() {
	$("#Home").addClass("current");
	$(".button").forEach(function(button) {
	    button.bind("touchstart", function() {
	    	button.addClass("touched");
	    });
	    button.bind("touchend", function() {
	    	button.removeClass("touched");
	    });
	});
});

Like jQuery and Zepto, you can also user $(document.ready({}):

var doSomething = function() {
   console.log(&quot;This is doing something. Cool, huh?&quot;);
}
$(document).read(function() {
	doSomething();
});

You can also use the simpler and more convenient format of a ChocolateChip function:

var doSomething = function() {
   console.log(&quot;This is doing something. Cool, huh?&quot;);
}
$(function() {
   doSomething();
});

Handling Events

Leave a comment

ChocolateChip provides a number of methods for dealing with events. They’re listed blow:

Element.bind()
Binds a event and callback to an element.
Element.unbind()
Removes and event and its callback from an element.
Element.removeEvents()
Sets all events on an element to null.

Element.bind()

This method binds the event to the element it is called on. It takes two arguments: the event enclosed in parentheses and a function to execute when the event occurs.

$.ready(function() {
    $("#home").bind("touchstart", function() {
        window.location = "http://css3wizardry.com"; 
    });
    $$("li").forEach("touchstart", function(item) {
        $(this.getAttribute("rel")).addClass("current");
    }
    $$(".button").forEach("touchstart", function(button) {
        button.addClass("touched");
    });
    $$(".button").forEach("touchend", function(button) {
        button.removeClass("touched");
    });
});

Element.unbind()

ChocolateChip provides an unbind method for removing any events from an element. This is especially important to do before removing/deleting any elements from your document in order to prevent memory leaks. There also may be the rare case where you only want the use to be able to perform an action once. In such a case, you can execute your code and as the last step unbind the event.

To unbind and event you need to pass in two arguments: the event that was bound and the name of the function that it executed. That means that if you were binding events with anonymous function, you will be unable to unbind them. There are several ways to work around this. This first is to declare all your functions separately with proper names. Or you could simply provide a name for the anonymous function while you are passing it to the event. You can do this in two ways:

$.ready(function() {
    $$(".button").forEach("touchstart", function touchedButton(button) {
        button.addClass("touched");
    });
    $$(".button").forEach("touchend", untouchedButton = function(button) {
        button.removeClass("touched");
    });
});

In the above example we have provided our events with function defined as arguments, like anonymous function, but these are both named. In the first example it is a normal named function. In the second example it is an anonymous function assigned to the global variable “untouchedButton.” Actually, in both cases these functions now reside in the global space. They are not private. Note that in the second example you cannot use the keyword “var” to assign the anonymous function to a variable. This would result in a parse error. With these functions now named we could remove them when necessary.

$.ready(function() {
function unbindEvents() {
    $$(".button").forEach(function(button) {
        button.unbind("touchstart", touchedButton);
        button.unbind("touchend"), untouchedButton);
    });
}	
window.onunload = unbindEvents;
});

Element.removeEvents()

If you really like anonymous functions, but you want a way to negate your events at some point, there is another alternative. ChocolateChip provides a utility function that parses all events attached to an element and sets them to null. It’s easy to execute. Just call it on the element.

$.ready(function() {
    $$(.button).forEach(function(button) {
        button.removeEvents();
    });
});

Event Delegation with Event Bubbling

If you have a collection of elements that you want to bind events to, instead of binding the event to each element, it would be more efficient to use event delegation to capture the events at a higher level. For example, if you have a list and you want to attach events to each list item you could do as follows:

$.ready(function() {
	$$("ul#menu li").forEach(function(item) {
            item.bind("touchstart", function() {
                item.addClass("touched");
            });
        });
});

This is fine to do if you have a few list items, but if your list has many items and you have many such lists throughout your application, this is very inefficient. It eats up memory and slows down your app. On top of that, if the list items are dynamic, you would have to write some event listeners to rebind events to any new items added to the list. And easier way is to attach an event handler to the list itself and listen for events on the list items. This way each list has only one bound event and it doesn’t matter how many list items there are or if any are removed or added. The event on the list will always capture any interaction with its child elements. In order to know what child element was the receiver of the event, we use the DOM’s target property. We can check whether it is a list item or not and then define what we want to do with it. Here’s an example:

$.ready(function() {
    $$("ul#menu").bind("touchstart", function(e) {
         if (e.target.nodeName.toLowerCase() === "li") {
               e.target.addClass("touched");
         }
    });
});

So, in the above code, we check to see if the target is a list item. If it is, we add a class to it. This simple pattern can be used whenever you have a collection of elements that you need to attach events to. Remember, attach an event to the parent of the collection and check the target to see if it is the element type you are interested in. If it is, do what you need to with it. Because we’re checking for list items, when the e.target is equal to a list item, we use e.target as the reference to the list item we wish to attach the class to.

The following code has both methods: attaching events to every list item and listening for events on the list itself:

$.ready(function() {
    // In this example events are attached to each list item:
    $$("ul#menu li").forEach(function(item) {
        item.bind("touchstart", function() {
            console.log(item.innerText);
        });
    });
    // In this example an event listener is attached to the list:
    $("ul#menu").bind("touchstart", function(e) {
         if (e.target.nodeName. === "LI") {
             console.log(e.target.innerText);
         }
    });
});

Both examples have the same result, touching a list item will output its content to the console. However, in the second version the code is more efficient because there is only one bound event. Remember that when using this technique of event delegation, you want to bind the event listener as close to the collection of elements as possible. This is because the evens bubble up from the target to where you’ve bound the event listener. In other words, binding all you event listeners to the body element would be extremely inefficient because all events would have to bubble all the way up to the body tag before you could capture them. So, attach you event listeners where it makes sense.

Update Nov 19, 2010

As of version 1.0.7, ChocolateChip has two new methods for delegating events and triggering events:

Element.delegate()

The Element.delegate() method takes three arguments: selector, event, callback. It attaches an event listener on the element to track the supplied event on its descendant nodes described by selector. When the designated event occurs, it executes the callback on the target element. The method passes the target element to the callback as an argument so that you have its scope inside the callback. Here’s an example of how to use the delegate method. Notice that we can reference the item that was touched by using the term item (could use any term here, it’s just a reference to the target element):

$("ul").delegate("li", "touchstart", function(item) {
    item.css("background-color: yellow");
});
$("ul").delegate("li", "touchend", function(item) {
    item.css("background-color: white");
});

Note: Jan 16, 2011

As of ChocolateChip version 1.0.9 the delegate method now works with elements having child nodes. This means that if you want to listen for an event on a complex control that has many descendant elements, the event will bubble up to the control to capture it. In previous versions the child elements would be the event target preventing delegation from working except on simple constructions. So, say you were delegating touch events for a list and the list items had text and images. With the earlier version of Element.delegate, if the user where to touch the images or text, the list item would not be the target of the event and the delegation would fail. Now this has been resolved. Now you can delegate events without worrying about whether the targets are simple or complex elements.

Element.trigger()

The element.trigger() method allows you to trigger an event on another element when interacting with another element, for example clicking on one element triggers a click on another element. To accomplish this, you execute the trigger method on the target element with the event you wish to trigger. Here’s how:

$("#link").bind("click", function() {
    console.log("You just clicked this link!");
});
// By clicking on #importantButton, a click will be triggered on #link button:
$("#importantButton").bind("click", function() {
    // Trigger an event on another element:
    $("#link").trigger("click");
});

New in ChocolateChip 2.0

Element.on()

This is equivalent in function to the same method in jQuery and Zepto. It expects at least an event type and a callback.

$(function() {
   $('#clickButton').on('click', function() {
      console.log('You just clicked this button!');
      console.log(this.nodeName) // returns name of node.
   };
});

You can also use the same method to implement a delegate event listener. The following example implements a delegate on the body tag to listen for a click on any element with a class of ‘button':

$(function() {
   $('body').on('click', '.button', function() {
      console.log('You just clicked this button!');
      console.log(this.nodeName) // returns name of node.
   };
});

Element.off()

This is the equivalent of unbind() and undelegate().

$(function() {
	// Normal event unbinding:
   $('#clickButton').off('click', 'buttonClickHandler');
   // Delegate unbinding:
   $('body').on('click', '.button', 'buttonClickHandler');
});

Traversing the DOM

Comments Off

DOM level 3 is a high quality precision tool to work with

The biggest pain in Web development is DOM traversal. You think you’re getting an element node, but you wind up getting an empty text node instead. Or worse still, there’s a bunch of them in your collection and it throws your calculations off. That’s why people use libraries like Prototype, jQuery, Mootools, YUI, Dojo, or any of the others. DOM level 3 finally gives us an unambiguous way to get an element node. ChocolateChip takes advantage of these new traversal tools to make it even easier to get around the DOM. Here’s the breakdown on what we’ve got:

  1. Element.firstElementChild
  2. Element.lastElementChild
  3. Element.previousElementSibling
  4. Element.nextElementSibling
  5. Element.children
  6. Element.childElementCount
  7. Element.first()
  8. Element.last()
  9. Element.previous()
  10. Element.next()
  11. Element.ancestor() New in version 1.1.1
  12. Element.ancestorByTag()
  13. Element.ancestortByClass()
  14. Element.ancestortByPosition()
  15. Element.contains()
  16. Element.isEqualNode()
  17. Element.isSameNode()

As of version 1.3.0, the following methods are available:

  1. Element.is()
  2. Element.not()
  3. Element.isnt() [same as Element.not()]
  4. Element.has()
  5. Element.hasNot()
  6. Element.hasnt() [same as Element.hasNot()]
  7. Element.childElements()
  8. Element.kids()
  9. Element.closest()

Element.firstElementChild

firstElementChild is a new property introduced in DOM level 3. It indicates a first child node that is an element, never an empty text node.

$(function() {
    var article = $("article");
    var firstParagraph = article.firstElementChild;
    firstParagraph.addClass("highlight");     
});

Element.lastElementChild

lastElementChild is a new property introduced in DOM level 3. It indicates a last child node that is an element, never an empty text node.

$(function() {
    var article = $("article");
    var lastParagraph = article.lastElementChild;
    lastParagraph.addClass("highlight");     
});

previousElementSibling is a new property introduced in DOM level 3. It indicates the previous sibling node that is an element, never an empty text node.

Element.previousElementSibling

$(function() {
    var section = $("section:first-of-type");
    // Get the last paragraph:
    var lastPara = section.lastElementChild;
    // Get the paragraph before the last paragraph:
    lastPara.previousElementSibling.addClass("highlight");   	
});

Element.nextElementSibling

nextElementSibling is a new property introduced in DOM level 3. It indicates the next sibling node that is an element, never an empty text node.

    var section = $("section:first-of-type");
    // Get the first paragraph:
    var firstPara = section.firstElementChild;
    // Get the paragraph after the first paragraph:
    firstPara.nextElementSibling.addClass("highlight");   

Element.children

children indicates the children of an element that are actual elements, leaving out any empty text nodes. This is an HTML collection, so it has a length property and can be iterated over with a loop. In contrast, the childNodes property returns all child nodes, including empty text nodes.

var children = $("article").children;
for (var i = 0; children.length < i; i++) {
    children[i].css("background-color: orange; color: red;");
}

Element.childElementCount

DOM level 3 provides a new property for indicating the number of element child nodes minus empty text nodes. Of course, you could also just query the length property of what the children property returns.

var howManyKids = $("article > section:nth-of-type(3)").childElementCount;
$("#childCount").fill("The number of child nodes is: " + howManyKids);

Element.first()

Element.first() is a ChocolateChip convenience method for getting the first child of an element. It’s less letters to type than the methods mentioned earlier, but with the same result.

// Get the last section's first child and fill it with text.
$("section:last-of-type").first().fill("This is the first paragraph.");

Element.last()

Element.last() is a ChocolateChip convenience method for getting the last child of an element. It’s less letters to type than the methods mentioned earlier, but with the same result.

// Get the 8th section's last child and add the class "important"
$("section:nth-of-type(8)").last().addClass("important");

Element.previous()

Element.previous() is a ChocolateChip convenience method for getting the previous sibling of an element. It’s less letters to type than the methods mentioned earlier, but with the same result.

// Get the second paragraph of #MainSection:
var mainParag = $("#MainSection p:nth-of-type(2)");
// Remove the class "secondary".
mainParag.removeClass("secondary");
// Add the class "special" to the paragraph before mainParag:
mainParag.previous().addClass("special");

Element.next()

Element.next() is a ChocolateChip convenience method for getting the next sibling of an element. It’s less letters to type in the methods mentioned earlier, but with the same result.

var firstItem = $("li:first-of-type");
firstItem.addClass("first-item");
firstItem.next().addClass("secondary");

Update: Feb 14, 2011

As of version 1.1.1, ChocolateChip now has a new method for getting the ancestor of a tag: Element.ancestor. The other ancestor methods, Element.ancestorByClass, Element.ancestorByTag and Element.ancestorByPosition, are deprecated and are now aliases to Element.ancestor.

Element.ancestor()

This method accepts one argument which can be one of four types: a position (numeric value), a tag, a class (delimited with an initial “.”) or and id (delimited with an initial “#”). This method can be used to get the ancestor of an element with the matching selector or to verify that an element is the descendant of an element with the supplied selector. In the case of a tag or class, Element.ancestor will return the first instance of the tag or class climbing up from the starting point. Any tag passed as an argument must be plain, meaning, without any attributes. You can test for the presence of an attribute on a tag by using a condition check on the returned node (see examples below).

// Will return the third ancestor tag:
var theAncestor = $("#someID").ancestor(3);
// Will return the element with id of "#main" 
// if it is an ancestor of "#someID":
var theAncestor = $("#someID").ancestor("#main");
// Will return an element with a class of ".myView" if
// it is an ancestor of "#someID":
var theAncestor = $("#someID").ancestor(".myView");
// Will return a subview tag if it is an ancestor of "#someID":
var theAncestor = $("#someID").ancestor("div");
// To find a tag with an attribute, use conditional checking:
var ancestorWithAttribute = $("#someID").ancestor("div");
if (ancestorWithAttribute.hasAttribute("rel") {
    console.log("This tag has a rel value of: " + ancestorWithAttribute.getAttribute("rel");
}

Element.ancestorByTag()

As of version 1.1.1, this tag is deprecated in favor of Element.ancestor() (see above)

Element.ancestortByTag() is a ChocolateChip method for getting the first instance of an ancestor of an element based on that ancestor’s tag.

$(".backButton").ancestorByTag("section").addClass("updated");

Element.ancestorByClass()

As of version 1.1.1, this tag is deprecated in favor of Element.ancestor() (see above)

Element.ancestorByClass() is a ChocolateChip method for getting the first instance of an ancestor of an element based on that ancestor’s class.

$(".nextButton").parentByClass("main").css("border: solid 2px red");

As of version 1.1.1, this tag is deprecated in favor of Element.ancestor() (see above)

Element.ancestorByPostion()

Element.ancestorByPostion() is a ChocolateChip method for getting the ancestor of an element at the designated position in the document. If no position is provided, it returns the immediate parent element. If the position provided is greater than the number of ancestor nodes for that element, it returns the body tag as the final ancestor.

<body>
    <article>
        <section>
            <header>
                <div class="button">
                              //....

$.ready(function() {
    console.log($(".button").ancestorByPosition().nodeName); // => HEADER
    console.log($(".button").ancestorByPosition(1).nodeName); // => HEADER
    console.log($(".button").ancestorByPosition(2).nodeName); // => SECTION
    console.log($(".button").ancestorByPosition(3).nodeName); // => ARTICLE
    console.log($(".button").ancestorByPosition(4).nodeName); // => BODY
    console.log($(".button").ancestorByPosition(20).nodeName); // => BODY
});

New methods since version 1.3.0:

Element.is()

This method returns the element if it matches the argument. The argument can be a tag, class or attribute.

Element.not()

This method returns the element if it does not match the argument. The argument can be a tag, class or attribute.

Element.has()

This method returns the element if one of its child nodes matches the argument. The argument can be a tag, class or attribute.

Element.hasNot()

This method returns the element if one of its child nodes does not match the argument. The argument can be a tag, class or attribute.

Element.childElements()

This method the child nodes of the element. If no argument is provided, it returns all of the element’s child nodes. If an argument is passed, it returns all child nodes that match the argument. Possible arguments are: tag, class or attribute.

Element.kids()

This is an alias for Element.childElements.

Element.closest()

This is an alias for Element.ancestor to match the function of the same name used by jQuery and Zepto.

Getting and Adding Text

Leave a comment

For people who like to mess with what’s on a page

Sometimes you need to get the text of an element, or add some text to an element. ChocolateChip provides a versatile set of tools for doing this. Here are the methods:

  1. Element.text()
  2. Element.fill()
  3. Element.empty()
  4. Element.innerText
  5. Element.outerText
  6. Element.textContent
  7. String.trim()
  8. String.capitalize()
  9. String.capitalizeAll()
  10. $.concat()

Element.text()

The $.text() method can serve you in two ways: it can get the text of the element if no value is passed as an argument. If a string of text is passed as an argument, $.text() will fill the element with that text. When $.text() gets text, it preserves the white space between nodes. If you really want to collapse the text and eliminate any white space, use the textContent property described below. Here are some examples of $.text() in action:

<section>
	<p>The first paragraph is always the best.</p>
	<p>Sometmes the second is good enough.</p>
	<p>But does anyone ever pay attention to the third paragraph?</p>
	<ul>
		<li>Butterflies</li>
		<li>Godzilla</li>
		<li>Care Bears</li>
		<li>Rattlesnakes</li>
		<li>Unicorns</li>
	</ul>
</section>
$(function() {
    // Will return "The first paragraph is always the best"
    console.log($("p:first-of-type).text());
    // Will change the contents of the third paragraph.
    $("p:nth-of-type(3).text("The third paragraph is different now.");
    // Get the text of the fourth list item.
    var listItem = $("li:nth-of-type(4)").text();
    // Change the fourth list item to "Anacondas"
    $("li:nth-of-type(4).text("Anacondas");
    console.log("The fourth list item was: " + listItem + ", but now it is: " + $("li:nth-of-type(4)").text());
});

Element.empty()

This method empties and element of all its child nodes.

$(function() {
    $("#response").empty();
});

Element.innerText

This method returns the text of the element with white space formatting. A single spece separates inline elements and a new line separates block elements.

<table>
	<tr>
		<td>Row One Item One</td>
		<td>Row One Item Two</td>
	</tr>
	<tr>
		<td>Row Two Item One</td>
		<td>Row Two Item Two</td>
	</tr>
</table>

$(function() {
    console.log($("table").innerText);
});

The above code would output the following to the console:

Row One Item One	Row One Item Two
Row Two Item One	Row Two Item Two

When using innerText to set the content of an element, be aware that it does not parse the string for HTML elements. Any HTML markup in the string is output as text. This is great if you want to prevent malicious script injection in your app.

Element.outerText

Like innerText, outerText returns the text of the target element’s parent node, which would include the text of any other child nodes that the parent has. Similarly, if you assign a text value to the element’s parent using outerText, all of the parent’s child nodes will be replaced with the new text. This is the same as the behavior of outerHTML.

Element.textContent

In contrast to innerText, textContent returns all of the whitespace, as if the content were in a set of pre tags.

$(function() {
    console.log($("table").textContent);
});

The above code would output the following to the console:

			Row One Item One
			Row One Item Two
		
		
			Row Two Item One
			Row Two Item Two

As you can see in the above example, textContent returned even the white space of the table row indentation. When using textContent to set the content of an element, anyy HTML markup in the string is output as text. This is different from what innerHTML does. This is great if you want to prevent malicious script injection in your app. Here’s an example of how textContent handles markup passed in as a string:

$(function() {
    var text = "<a href='#' onclick='doEvilThings()'>Please click here</a>";
    $("p:first-of-type").textContent = text;
});

This gets output as:

<a href='#' onclick='doEvilThings()'>Please click here</a>

Instead of something like this: Please click here

As you can see, the malicious code has been rendered as plain text. Life is good when the bad guys lose.

String.trim()

This method removes any white space at the beginning and end of a string.

<ul>
		<li>item 1</li>
		<li>item 2</li>
		<li>item 3</li>
		<li>item 4</li>
		<li>     item 5     </li>
	</ul>
$(function() {
    // Will return: "item 5" with no white space.
    var text = $("p:last-of-type").text().trim();
});

Update Nov 19, 2010

As of version 1.0.7, ChocolateChip has two new methods for capitalizing strings: String.capitalize() and String.capitalizeAll(). String.caplitalize() capitalizes the first letter of the string, regardless of whether it has multiple words. String.capiatlizeAll() capitalizes the first letter of every word in the string. Here how you use them:

var sstr = "this is a string";
console.log(sstr.capitalize()); // => This is a string
console.log(sstr.capitalizeAll()); // => This Is A String

As of version 1.3.0, the following method is available:

$.concat()

In general, string concatenation is not very performant in JavaScript. However, using the Array.join method creates a string from an array very efficiently. $.concat() uses arrays or array like objects to perform efficient string concatenation. You pass an array of strings as the argument.
If you have an array-like object, you can convert it to an array using $.slice() (see example below).

var users = ["<li>Item One</li>","<li>Item Two</li>","<li>Item Three</li>","<li>Item Four</li>","<li>Item Five</li>"];
users.prependTo("#users > ol");
// Get the arguments object to prepend:
function outputString(){
    var str = $.slice(arguments);
    str.prepend("#message");
}
outputString("This"," ","is"," ","a"," ","tedious"," ","example",".");

Insert Whatever, Wherever You Want

Leave a comment

ChocolateChip simplifies inserting content into a document

DOM level 3 has some new ways of injecting content into a document. And on top of that ChocolateChip has its own handy utility methods for inserting content. Here’s the goods:

  1. Element.insertAdjacentText()
  2. Element.insertAdjacentHTML()
  3. Element.insertAdjacentNode()
  4. $.make()
  5. Element.insert()
  6. Element.before()
  7. Element.after()
  8. Element.fill()
  9. Element.prepend()
  10. Element.append()

All three of the insertAdjacent methods take two parameters, the place to insert and the content to insert. Passing in the correct parameter for position is very important for getting your content where you want it. This means that these insert methods do not replace the contents of the element with the content you pass in. Instead they insert it in juxtaposition to the element. You have four options for where you want to insert. IMPORTANT! These arguments must be in quotes or you’ll get an exception.

“beforeBegin”
This inserts the content immediately before the element so that the content become the previous sibling of the element.
“afterBegin”
This inserts the content into the element as its first child. This does not replace any other content already in the element.
“beforeEnd”
This inserts the content into the element as its last child. This does not replace any other content already in the element.
“afterEnd”
This inserts the content immediately after the element so that the content become the next sibling of the element.

Element.insertAdjacentText()

This method insert a string of text at the location passed in.

<p id="description">This is the main section.</p>
$(function() {
	$("#description").insertAdjacentText("beforeBegin", "This is before the description: ");
	$("#description").insertAdjacentText("afterBegin", "This is at the beginning of the description: ");
	$("#description").insertAdjacentText("beforeEnd", "This is at the end of the description: ");
	$("#description").insertAdjacentText("afterEnd", "This is after the end of the description: ");
});

Element.insertAdjacentHTML()

This is similar to the Element.insertAdjacentText() method, except that it creates HTML which it then inserts at the specified locations.

$(function() {
	$("#description").insertAdjacentHTML("beforeBegin", "<p>This is before the description: </p>");
	$("#description").insertAdjacentHTML("afterBegin", "<p>This is at the beginning of the description: </p>");
	$("#description").insertAdjacentHTML("beforeEnd", "<p>This is at the end of the description: </p>");
	$("#description").insertAdjacentHTML("afterEnd", "<p>This is after the end of the description: </p>");	
});

Element.insertAdjacentNode()

Like the previous two methods, this method inserts content into the designated locations, except that it uses existing nodes. You could pluck nodes from elsewhere in a document, or create them on the fly for inserting.

$(function() {
	var a1 = $.make("<p>First Paragraph</p>");
	var a2 = $.make("<p>Second Paragraph</p>");
	var a3 = $.make("<p>Third Paragraph</p>");
	var a4 = $.make("<p>Fourth Paragraph</p>");
	$("#description").insertAdjacentElement("beforeBegin", a1);
	$("#description").insertAdjacentElement("afterBegin", a2);
	$("#description").insertAdjacentElement("beforeEnd", a3);
	$("#description").insertAdjacentElement("afterEnd", a4);	
});

$.make()

$.make() is a method for creating HTML element nodes on the fly. It takes as its argument a string of valid markup. This must be correct syntax for the doctype used by the target document or the browser will not render it properly.

$(function() {
    var content = $.make("<p>This is a paragraph with some <strong>important</strong> and <em>trivial</em> information.</p>");
     var menu = $.make("<ul id="menu"><li>Item One</li><li>Item Two</li><li>Item Three</li></ul>");
});

$.insert()

The method does what the label say. It allows you to insert content into another element. The first argument is the content to insert. The second, optional argument is the position at which to insert the content. If no position is supplied it default to inserting the content at the end. As a convenience you can pass in “first” or “last” for a position. Otherwise a digit starting from 1 to indicate the position at which to insert. 1 is equivalent to “first.” If the numerical position passed as an argument is larger than the number of child nodes in the target element, it will be inserted in the last position. If the content passed in is a string, the method attempts to convert it into HTML nodes before inserting. If the content consists of HTML nodes, the method inserts them at the indicated position.

$(function() {
    // Insert this at the beginning:
    $("#response").insert("<p>A new paragraph.</p>", "first");
    // Insert this at the end:
    $("#response").insert("<p>Another paragraph at the end.</p>", "last");
    // Insert this at position 5, that is, as the fifth child of the element:
    $("#response").insert("<p>An important update.</p>", 5);
});

$.before()

This method inserts content before the target element. The inserted content thus becomes the target’s previous sibling. The content passed in can be a node or an array of nodes.

$(function() {
     var newNode $.make("<h1>An Important Announcement</h1>");
    $("body > p").before(newNode);
});

$.after()

This method inserts content after the target element. Thus the content becomes the next sibling of the target. The content passed in can be a node or an array of nodes.

$(function() {
     var newNode $.make("<li>The last item is always the most important!</li>");
    $("ul#importantList").after(newNode);	
});

$.fill()

This method replaces the contents of the element with the content passed as an argument. Unlike other methods above, this method does not attempt to convert a string into HTML before insertion. Rather it is primarily for replace the text content of an element with the text passed in. However, if a node is passed it, it will insert it into the element. This method always replaces whatever the content of the element is, even if that consists of multiple child nodes.

$(function() {
    $("#update").fill("This page has just been updated");
});

Element.prepend()

As of version 1.1.9 ChocolateChip offers the element.prepend method. This allows you to insert content at the beginning of a collection of nodes. If the parent is empty, the content will simply be inserted. This is just a convenience method to mimic the jQuery equivalent.

$(".menu").prepend("<li>item 1</li><li>item 2</li><li>item3</li>");

Element.append()

As of version 1.1.9 ChocolateChip offers the element.append method. This allows you to insert content at the end of a collection of nodes. If the parent is empty, the content will simply be inserted. This is just a convenience method to mimic the jQuery equivalent.

$(".menu").append("<li>Final item here</li>");

Adding Some Class to Your Document

Comments Off

ChocolateChip provides several methods working with classes

  1. Element.hasClass()
  2. Element.addClass()
  3. Element.removeClass()
  4. Element.toggleClass()

Element.hasClass()

Sometimes you just need to check an element to see if it has a class or not. To accomplish this use the Element.hasClass() method like this:

$.ready(function() {
    // We want the third paragraph in the document:
	var p = $("p:nth-of-type(3);
	if (p.hasClass("important")) {
		p.fill("This is very important!");
	}
	// Get all list items and check to see if they have the class "medium":
	$$("li").forEach(function(item) {
		if (item.hasClass("medium") {
			item.css("font-size: 14px");
		} else {
			item.css("font-size: 20px");
		}
	});
});

Element.addClass()

This method does what the label says, it adds a class to the element:

// Here we're getting the all the list items of the second ul in the docuemnt:
$$("ul:nth-of-type(2) > li").forEach(function(item) {
	item.addClass("subcategory");
});

Element.removeClass()

Use the Element.remove() method to, well, remove a class:

$.ready(function() {
	$$("a").forEach(function(link) {
		if (link.hasClass("external")) {
			link.removeClass("External");
		}
	});
});

Element.toggleClass()

And now, the best of all, the class toggler method. This method works differently depending on whether you pass in one or two classes to toggle. If a single class is passed, toggleClass() will add the class if the element doesn’t have it, or remove it if the element already has it. However, If you pass in two classes, toggleClass() will check the element to see if it has the first class. If it does, it will remove that class and added the second class. If the element doesn’t have the first class, toggleClass() will added it. Then the next time toggleClass is called, it will toggle the two classes on the element.

$.ready(function() {
	$$("li").forEach(function(item) {
		item.bind("touchestart", function() {
			item.toggleClass("touched");
		};
		item.bind("touchend", function() {
			item.toggleClass("touched");
		};
	});
	$$("a").forEach(function(link) {
		link.bind("touchstart", function() {
			link.toggleClass("touched", "untouched");
		};
	});
});

Getting and Setting an Element’s Style

Comments Off

ChocolateChip version 1.1.7

CSS() update

As of version 1.1.7 element.css() has been updated to accept arguments more like jQuery. You no longer need to enclose a string of CSS declarations in curly braces. You can just quote the string. This means that if you have code written with curly braces on for element.css() you’ll need to update that code to work with this version. You can also pass element.css() and object of property/value pairs. When passing an object, if the property is a single word, it does not need to be quoted. If it is a hyphenated property it does need to be quotes. The property values always need to be quoted. Observe the examples below:

// Example of string:
$(".content").css("height: 100%; width: 100%, background-color: #ccc; color: #666; border: solid 2px #666;");

// Simple property and value (can only use once, for more use object notation)
$(".menu").css("border", "solid 1px red");

// Passing in an object of properties and values:
$(".footer").css({height:"100px", width:"100%", color:"#000","background-color":"#fff});

// To get a particular style attribute of an element:
var menuFontColor = $(".menu > li").css("color");

Update Dec 23, 2010

As of version 1.0.8, ChocolateChip no longer has a separate $.style() method. Its functionality has been incorporated into the Element.css() method. The new functionality is explained as follows:

To get the style of an element, simply pass the css() method the property whose value you want:

console.log("The font size is: " + $("#item").css("font-size"));

Note: When ChocolateChip returns a CSS value, this is always a string. This is so even when it appears to be a numerical value such as height or width. To do any math operation with these types of returned values, you’ll need to convert them to numbers. You can do this with parseInt. If you do not use parseInt, your attempts at addition will instead result in string concatenation. The example below shows how to use parseInt with a returned CSS value to get the numerical equivalent.

var itemWidth = $("#item").css("width");
itemWidth = parseInt(itemWidth);
var distance = itemWidth + $("#item").getLeft();
console.log("The total distance from the left is: " + distance);

There are two ways to set styles on an element. You can set a property/value pair or a string of values delimited by curly braces and quoted. By enclosing the curly braced text we can pass in the complex CSS definitions as a string. At parse time, ChocolateChip removes the curly quotes and applies the CSS definition to the element:

// Example of property/value pair:
$("p:first-of-type").css("color", "red");

// Example of string enclosed in curly braces and quotes:
$$("li.members").forEach(function(members) {
   members.css("{ color: red; background-color: orange; font-size: 24px; font-weight: bold; }");
});

For versions 1.0.7 and earlier ChocolateChip uses the following two methods to get and set styles:

ChocolateChip has two methods for dealing with style. They are:

  1. $.style()
  2. Element.css()

$.style() takes two arguments: the element whose style you want, and the style you want:

$.ready(function() {
	var p = $("p:first-of-type");
	var pColor = $.style(p, "color");
	console.log("The text color is: " + pColor);
});

Setting an element’s style is easy using the Element.css() method. This method takes as its argument any valid CSS property/value pairs. By default this method adds the CSS to whatever is already defined on the element. If you want, you can pass an option second parameter of true. This will force the method to replace whatever inline styles the element might have with the styles you are passing in.

$.ready(function() {
	var p = $("p:first-of-type");
	var styles = "font-weight: bold; background-color: red; color: gold;";
	// This will append the new CSS onto the paragraph tag.
	p.css(styles);
	// In contrast, this will replace whatever other styles might be inline on the paragraph with the styles being applied.
	p.css("font-size: 24px; text-shadow: 0 1px #fff; color: #000;", true);
});

As you can see in the above examples, you can assign the CSS to a variable and pass that in, or you can define the CSS in the method itself, your choice.

Attack of the Clones

Comments Off

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

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

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

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.

Older Entries

Follow

Get every new post delivered to your Inbox.