Traversing & Manipulating
jQuery provides powerful tools for finding the element or elements you're after, and then working with those elements to achieve a desired result. Through its traversal and manipulation methods, it takes tasks that would be rather painful to achieve using native DOM manipulation, and makes those tasks fairly straightforward and intuitive.
In this chapter, we'll take a look at some (but not all) of those traversal and manipulation methods. Before we do, though, there are a few important vocabulary terms that you should know. Let's consider a bit of HTML:
1
2
3
4
5
6
7
8
<ul>
<li>
<span>
<i>Foo</i>
</span>
</li>
<li>Bar</li>
</ul>
- The first list item is a child of the unordered list.
- The unordered list is the parent of both list items.
- The span is a descendant of the unordered list.
- The unordered list is an ancestor of everything inside of it.
- The two list items are siblings.
Traversal
jQuery lets us "traverse" — or move through — the HTML elements that make up our page. First, we make an initial selection, and then move through the DOM relative to that selection. As we move through the DOM, we're altering our original selection; in some cases, we're replacing the original selection with the new selection, while in other cases, we're adding to or subtracting from the original selection.
Filtering selections
You can filter an existing selection to only include elements that match a certain criteria. For example, you can filter a selection in the following ways:
1
2
3
4
5
6
7
8
9
10
var listItems = $( 'li' );
// filter the selection to only items with a class of 'special'
var special = listItems.filter( '.special' );
// filter the selection to only items without a class of 'special'
var notSpecial = listItems.not( '.special' );
// filter the selection to only items that contain a span
var hasSpans = listItems.has( 'span' );
Importantly, note that .not()
is not the opposite of .is()
. The .is()
method returns a boolean, while the .not()
method returns a new jQuery
object.
Finding elements relative to a selection
An initial selection can serve as the "home base" for making additional selections; for example, you may have an existing selection that contains an individual list item, and then need to work with its siblings or the unordered list that contains the item. You can make a new selection relative to an existing selection easily:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// get the first list item on the page
var listItem = $( 'li' ).first(); // also: .last()
// get the siblings of the list item
var siblings = listItem.siblings();
// get the next sibling of the list item
var nextSibling = listItem.next(); // also: .prev()
// get the list item's parent
var list = listItem.parent();
// get the list items that are immediate children of the list
var listItems = list.children();
// get ALL list items in the list, including nested ones
var allListItems = list.find( 'li' );
// find all ancestors of the list item that have a class of "module"
var modules = listItem.parents( '.module' );
// find the closest ancestor of the list item that has a class of "module"
var module = listItem.closest( '.module' );
You can also add to an existing selection by using the .add()
method.
You can pass it a selector, an array of elements, a string of HTML, or a jQuery
object.
1
2
3
4
5
var list = $( '#my-unordered-list' );
// do some stuff with the list, and then ...
var listAndListItems = list.add( '#my-unordered-list li' );
Getting back to your original selection
When you use one of the traversal methods to find some elements relative to an initial selection, jQuery stores a reference to your initial selection in case you want to get back to it. For example, consider a case where you select an unordered list, make some changes to its list items, and then want to work with the unordered list again. You can use the jQuery .end()
method to get back to your original selection:
1
2
3
4
5
6
7
8
9
10
$( '#my-unordered-list' )
.find('li')
// now we're working with the list items
.addClass('special')
.end()
// now we're back to working with the list
.addClass('super-special');
The .end()
method makes it easy to make a lot of changes in a single statement. This practice does little for code clarity, though; it's a little like telling a story without stopping to take a breath. Because of this, you should use it sparingly. More often than not, it will lead to code that's difficult to read, maintain, and debug.
A better solution might look like this:
1
2
3
4
5
var list = $( '#my-unordered-list' );
var listItems = list.find('li');
listItems.addClass( 'special' );
list.addClass( 'super-special' );
jQuery also provides the .addBack()
method if you want to add your original selection to your current selection. For example:
1
2
3
4
5
6
7
8
9
10
11
$( 'li.special' )
.siblings()
// now we're working with the siblings of the original selection
.removeClass( 'important' )
.addBack()
// now we're working with the original li's AND their siblings
.addClass( 'urgent' );
Confused? Just like the .end()
method can result in code that's difficult to work with, the .addBack()
method — while sometimes useful — can easily lead to complex code. A better solution would use the .add()
method to combine two original selections instead:
1
2
3
4
5
var specialListItems = $( 'li.special' );
var otherListItems = specialListItems.siblings();
otherListItems.removeClass( 'important' );
specialListItems.add( otherListItems ).addClass( 'urgent' );
There are several traversal methods we haven't covered here; you can read about all of the traversal methods in the traversing documentation.
Manipulation
jQuery's manipulation methods allow you to alter the DOM of your page using a syntax that's much friendlier than the one provided by native DOM manipulation methods. Manipulation methods return the jQuery object on which they were called, which means you can chain them or combine them with other jQuery methods such as the ones discussed above.
Altering elements
There are myriad ways to alter elements using jQuery. Here, we'll look at how to achieve some of the most common tasks.
Adding and removing classes
The class attribute on an element can be used to target CSS rules, and can also
be a useful way to target jQuery selections. For example, an element may have a
class of hidden
, with a corresponding CSS rule that causes elements with that
class to have their display
set to none
. Using jQuery, we can add and
remove classes to affect the display of elements.
1
2
$( 'li' ).addClass( 'hidden' );
$( 'li' ).eq( 1 ).removeClass( 'hidden' );
If your use case requires adding and removing a class repeatedly, jQuery provides the .toggleClass()
method. The following code adds the class hidden
if it is not present, and removes it if it is present.
1
$( 'li' ).eq( 1 ).toggleClass( 'hidden' );
Changing style
When you can't achieve your goal via adding and removing classes, jQuery
provides the .css()
method to allow you to set the style of elements
directly. This is usually required when you want to set numeric values that can
only be calculated at runtime — for example, positioning information. The
.css()
method should not be used to set simple styles, such as display:
none
— in almost all cases, it is preferable to achieve these changes via
classes and CSS.
For example, consider the case where you want to style an element based on the
width of its parent element; it might be difficult or impossible to know the
width of the parent element ahead of time in a flexible layout. In this case,
we might resort to the .css()
method for styling.
1
2
3
4
5
6
7
var list = $( '#my-unordered-list' );
var width = Math.floor( list.width() * 0.1 );
list.find('li').each(function( index, elem ) {
var padding = width * index;
$( elem ).css( 'padding-left', padding + 'px' );
});
If you need to set multiple properties at once, you can pass an object to the
.css()
method rather than a property name and a value. Note that you will
need to quote any property names that include a hyphen.
1
2
3
4
$( 'li' ).eq( 1 ).css({
'font-size': '20px',
'padding-left': '20px'
});
Changing form values
jQuery provides the .val()
method for altering the value of form elements
such as select
and input
elements.
For text input
elements, you can set their content by passing a string to the
.val()
method:
1
$( 'input[type="text"]' ).val( 'new value' );
For select
elements, you can set the chosen option using .val()
as well:
1
$( 'select' ).val( '2' );
For checkbox input
elements, you'll need to set the checked
property on
the element using the .prop()
method.
1
$( 'input[type="checkbox"]' ).prop( 'checked', 'checked' );
.prop()
method was introduced in jQuery 1.6; prior versions of jQuery used the .attr()
method for this purpose. It continues to work in later versions of jQuery, but in the case of the checked
property, it ultimately just calls the .prop()
method. If you are using a version of jQuery later than 1.6, you should always use the .prop()
method to set the checked
property and other DOM element properties. See the documentation for a more detailed explanation.
Changing other attributes
You can use jQuery's .attr()
method to change other attributes of elements.
For example, you can use it to set a new title
attribute for a link:
1
$( 'a' ).attr( 'title', 'Click me!' );
When setting an attribute, you can pass a function as the second argument. Just like other setter methods, this function receives two arguments: the index of the element on which it's operating, and the original value of the attribute. This function should return the new value for the attribute.
1
2
3
$( 'a' ).attr( 'href', function(index, value) {
return value + '?special=true';
});
You can also remove attributes entirely using .removeAttr()
.
Getting information from elements
In the jQuery basics chapter, we discussed the notion
of "getter" and "setter" methods. All of the methods that can be used to
change elements can also be used to get information from elements. For example,
the .val()
method described above can be used as both a setter and a
getter.
1
2
3
var input = $( 'input[type="text"]' );
input.val( 'new value' );
input.val(); // returns 'new value'
Likewise, the .css()
method can be used to retrieve the value of individual
CSS properties by passing only a property name, not a value.
1
var listItemColor = $( 'li' ).css( 'color' );
When manipulation methods are used as getters, the method operates on only the
first element in the selection, with one notable exception: the .text()
method. In the case of the .text()
method, the text of all selected
elements will be returned if no argument is passed to the method.
Placing elements in the document
Whether you've selected an element or created a new element, you can take your selection and place it in your document. There are generally two ways to do this: by calling a method on the element(s) you want to place, or by calling a method on the element relative to which you want to place it.
For example, consider the case where you want to move the first list item in a list to the end of the list. There are several ways to achieve this.
You could append the item to the list by calling .appendTo()
on the list
item:
1
2
var listItem = $( '#my-unordered-list li' ).first();
listItem.appendTo( '#my-unordered-list' );
You could append the item to the list by calling .append()
on the list:
1
2
var listItem = $( '#my-unordered-list li' ).first();
$( '#my-unordered-list' ).append( listItem );
You could insert the list item after the last list item by calling
.insertAfter()
on the list item that you want to move:
1
2
var listItems = $( '#my-unordered-list li' );
listItems.first().insertAfter( listItems.last() );
You could also insert the list item after the last list item by calling
.after()
on the last list item:
1
2
var listItems = $( '#my-unordered-list li' );
listItems.last().after( listItems.first() );
There are many other methods for placing elements — you can place them around, inside, and outside of other elements, depending on your specific needs.
The most effective way to place an element depends on the elements to which you already have access. In the example above, you might choose to append the list item to the unordered list if you have already selected the unordered list for some other purpose; or, if you already had a selection containing all of the list items, then placing the first list item relative to the last list item might be the easiest path.
When choosing how to place an element, you should consider not only the easiest way to place it, but also the most maintainable way. Beware of placements that make assumptions about the exact structure of your page's HTML.
Copying elements
You can make a copy of an element or a set of elements using jQuery's
.clone()
method. This will make a copy of the elements, but note that the
copy is only in memory — you will need to place it in the document yourself.
You can manipulate the cloned element or elements before placing them into the
document.
1
2
3
4
5
6
7
var clones = $( 'li' ).clone();
clones.html(function( index, oldHtml ) {
return oldHtml + '!!!';
});
$( '#my-unordered-list' ).append( clones );
id
attribute before inserting it into the document, as a
document should never have more than one element with a particular ID.
Removing elements
There are three ways to remove elements from the document: .remove()
,
.detach()
, and .replaceWith()
. Each method serves a particular purpose.
The .remove()
method should be used to remove elements permanently, as it
will also unbind any event handlers attached to the elements being removed. The
.remove()
method returns a reference to the removed elements, but if you
re-add the removed elements, the removed elements will no longer have events
bound to them.
1
2
3
4
5
6
7
8
$( '#my-unordered-list li' ).click(function() {
alert( $( this ).text() );
});
var removedListItem = $( '#my-unordered-list li' ).first().remove();
removedListItem.appendTo( '#my-unordered-list' );
removedListItem.trigger( 'click' ); // no alert!
The .detach()
method is useful for temporarily removing elements from the document; for example, if you are going to make a lot of changes to your page's structure using jQuery, it will be more efficient to use .detach()
to remove the affected elements, make your changes, and then re-attach the element using one of the insertion methods. Elements removed with .detach()
will retain their event handlers; you can re-add them to the document with .appendTo()
or another DOM insertion method.
1
2
3
4
5
6
7
8
9
10
$( '#my-unordered-list li' ).click(function() {
alert( $( this ).text() );
});
var detachedListItem = $( '#my-unordered-list li' ).first().detach();
// do something complicated with the list item
detachedListItem.appendTo( '#my-unordered-list' );
detachedListItem.trigger( 'click' ); // alert!
Finally, the .replaceWith()
method replaces an element or elements with the
element or HTML passed as an argument to .replaceWith()
. The replaced
elements are returned, but just like with .remove()
, all event handlers are
unbound from the replaced elements.
1
2
3
4
5
6
7
8
9
10
$( '#my-unordered-list li' ).click(function() {
alert( $( this ).text() );
});
var replacedListItem = $( '#my-unordered-list li' ).first()
.replaceWith( '<li>new!</li>' );
replacedListItem.appendTo( '#my-unordered-list' );
replacedListItem.trigger( 'click' ); // no alert!
Conclusion
In this section, we looked at various ways we can move through a document, move elements and place new elements in the document, and make changes to elements. In the next section, we'll look at how to listen for user interaction with our page.