Welcome to our free jQuery tutorial. This tutorial is based on Webucator's jQuery Fundamentals Training course.
In this lesson, we will introduce jQuery basic concepts, including working with selections and manipulating the DOM.
Lesson Goals
Most jQuery applications will follow a sequence of steps like:
The first section of this lesson deals with making selections. Since we need
to affect our selections somehow in order to see what was actually selected,
we will limit ourselves to using the addClass(className)
method
on our selections at first. Later in the lesson we will explore some of the
many functions jQuery provides to modify selected elements.
$()
You cannot safely manipulate a page until the document is "ready." jQuery
detects this state of readiness for you; code included inside $()
will
only run once the page is ready for JavaScript code to execute as the following demo illustrates:
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <script src="../../jqy-libs/jquery.js"></script> <script> $(function() { $('output').html('Ready!'); }); </script> <link rel="stylesheet" href="../normalize.css"> <link rel="stylesheet" href="../styles.css"> <title>document Ready</title> </head> <body> <main> <p>We'll let you know when the document is ready ...</p> <output id="output"></output> </main> </body> </html>
When the document fully loads, $()
will be fired, resulting in "Ready" being injected into the HTML output
element.
$(document).ready()
is another form of $()
. For example, the two blocks of code below are equivalent:
$(function() { $('output').html('Ready!'); });
$(document).ready(function() { $('output').html('Ready!'); });
While you're likely to see $(document).ready()
used in a lot of existing jQuery code, you should not use it in your own as it has been deprecated as of version 3 of jQuery.
You can also pass a named function to $()
instead of passing an anonymous function:
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <script src="../../jqy-libs/jquery.js"></script> <script> function readyFn() { $('output').html('Ready!'); } $(readyFn); </script> <link rel="stylesheet" href="../normalize.css"> <link rel="stylesheet" href="../styles.css"> <title>$() Passing Named Function</title> </head> <body> <main> <p>We'll let you know when the document is ready ...</p> <output id="output"></output> </main> </body> </html>
Again, the first thing you usually do with jQuery is locate one or more elements on the page. This is done with the jQuery()
function, which can also be written $()
. Simply pass in a CSS selector to this function and you will get all the elements on the page that match that selector as a collection of elements.
jQuery supports most CSS selectors, as well as some non-standard selectors. For a complete selector reference, visit https://api.jquery.com/category/selectors/.
$('#myId'); // note IDs must be unique per page
$('.myClass'); // all tags with the class myClass $('div.myClass'); // performance improves if you specify element
$('input[name=first-name]');
$('#contents ul.people li');
$('#myId').addClass('red');
Pseudo-selectors are special terms prefixed with a colon ( :
)
symbol, which select based on some aspect of an element that jQuery can determine.
Many of the available selectors are listed below.
Some of these are defined by CSS; others are specific to jQuery.
Selector | Description |
---|---|
:animated |
Currently being animated by jQuery |
:visible |
Currently visible |
:hidden |
Not currently visible |
:focus |
Currently has focus |
Selector | Description |
---|---|
:button |
<input type="button"> or <button> |
:checkbox |
<input type="checkbox"> |
:file |
<input type="file"> |
:image |
<input type="image"> |
:input |
Any form control (<input> , <select> ,
<textarea> tags) |
:password |
<input type="password"> |
:radio |
<input type="radio"> |
:reset |
<input type="reset"> |
:submit |
<input type="submit"> |
:text |
<input type="text"> or <textarea> |
:disabled |
Any disabled form element |
:enabled |
Any enabled form element |
:checked |
Any checked element (radio or checkbox) |
:selected |
Any <option> elements that are selected |
Selector | Description |
---|---|
:empty |
Has no children |
:contains(text) |
Contains the specified text |
:not(subselector) |
Does not match the subselector |
:has(selector) |
Contains one or more elements found by the subselector |
:first-child |
Is the first child of its parent |
:last-child |
Is the last child of its parent |
:only-child |
Is the only child of its parent |
:nth-child(index) |
Is the child at the nth position within its parent |
:header |
Is one of the header types (<h1> , <h2> ,
etc.) |
Selector | Description |
---|---|
:first |
The first element found by the selector |
:last |
The last element found by the selector |
:even |
Even-index elements within the collection -- the first element is even, since its index is 0 |
:odd |
Odd-index elements within the collection |
:eq(index) |
At the index position within the collection |
:gt(index) |
Position within the collection is greater than index |
:lt(index) |
Position within the collection is less than index |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <script src="../../jqy-libs/jquery.js"></script> <script> $(function() { $('tr:odd').addClass('odd'); // select all input-like elements in a form $('#myForm :input').addClass('yellowBg'); // select all the visible elements $('div:visible').addClass('italic'); // now unhide the hidden div - it should not be italic $('div:hidden').removeClass('noShow'); // all except the first three divs $('div:gt(3)').addClass('redText'); // all currently animated divs (not implemented here) $('div:animated'); }); </script> <style> .odd { background-color: #eeeeff; } .yellowBg { background-color: yellow; } .italic { font-style:italic; } .redText { color: red; } .noShow { display: none; } </style> </head> <body> <div> <table border="1" cellspacing="0" cellpadding="8"> <thead> <tr> <th>Name</th> <th>Age</th> </tr> </thead> <tbody> <tr> <td>Sue</td> <td>22</td> </tr> <tr> <td>Bill</td> <td>25</td> </tr> <tr> <td>Tom</td> <td>44</td> </tr> </tbody> </table> </div> <div> <form id="myForm"> <input type="text" /> <textarea>Hello</textarea> <select> <option>A</option> <option>B</option> </select> </form> </div> <div class="noShow">Not initially visible</div> <div>The fourth div</div> <div>The fifth div</div> <hr /> </body> </html>
Choosing good selectors will improve the performance of your JavaScript. You should always consider the browser's JavaScript tools available to jQuery to process selectors:
document.getElementById(id)
- very highly optimized, so a query
starting with an id is always best. A tag-qualified id (div.#myId
) is actually
less efficient, and should be avoided unless you want to ensure that the element
is indeed the specified tag type.
element.getElementsByTagName(tagName)
- optimized
by browsers, even for descendants that are not direct children. Use this to reduce
the set of tags to be considered for more time-consuming operations, like those
based on attributes or content.
element.querySelectorAll(selector)
- optimized
by browsers, and used by jQuery when available and applicable. It appears that
the current implementation of jQuery will only use this for an entire selector,
as opposed to using it for part of a selector string, and then applying additional
logic, so it might be more efficient when using a jQuery-only pseudo-selector
to apply it as a separate operation (i.e., invoke the corresponding method
on a collection obtained by a selector that can use querySelectorAll
)
Inspecting attributes and content - generally time-consuming,
and not necessarily applied in an efficient fashion, so it is best to try to
reduce the number of tags considered by this part of the selection process.
For example, $('input[name=q]')
is more efficient than $('[name=q]')
,
since only input tags need to be inspected for their name.
A little specificity, for example, including an element
type such as div
when
selecting elements by class name, can go a long way. If you know that the
CSS class myClass
was only applied to div
tags, then $('div.myClass')
is
more efficient than $('.myClass')
, since jQuery can use getElementsByTagName('div')
before
inspecting the class
attribute, reducing the number of tags to
consider. Generally, any time you can give jQuery a hint about where it might
expect to find what you're looking for, you should.
On the other hand, too
much specificity can be a bad thing. A selector such as #myTable
thead tr th.special
is
overkill if a selector such as #myTable
th.special
will get you what you want. Part of efficient jQuery is
designing the HTML in a way that makes optimized selection possible.
jQuery offers many attribute-based selectors, including the ability to make selections based on the content of attributes using simplified regular expressions.
<html lang="en"> <head> <script src="../../jqy-libs/jquery.js"></script> <script> $(function() { // find all <p>s whose class attribute // ends with "text" $("p[class$='Text']").addClass("italic"); }); </script> <style> .italic { font-style:italic; } .redText { color: red; } .blueText { color: blue; } .greenText { color: green; } .yellowBg { background-color: yellow; } </style> </head> <body> <p class="redText">I have red text.</p> <p class="blueText">I have blue text.</p> <p class="greenText">I have green text.</p> <p class="yellowBg">I'm a p tag with some other class.</p> <p>I'm an ordinary p tag.</p> </body> </html>
While these can be useful in a pinch, they can also be extremely slow. Wherever possible, make your selections using IDs, class names, and tag names.
Also, possibly the biggest drawback, at least as far as using this approach
with classes, is that it tests the ending of the entire string, not the end
of each individual class name. So, for example, <p class="redText
special">
would not be found by [class$=Text]
.
You should be careful when choosing a selector to avoid selecting more elements
than you want. In particular, with elements that can contain like elements
as descendants (like lists within lists, or tables within tables), this can
be a problem. The best approach is to use an id selector first, if possible,
to select the container for the elements you want to select, and then the CSS child
selector (>
) between the container and the children you
want to select.
// Bad - will select great-grandchildren if there are inner lists $('#myList li').remove(); // Good - will only select li children $('#myList>li').remove();
Also, you should take precautions to prevent future changes to the HTML from
breaking your code, or causing unwanted effects. For example, if a form currently
has only one select
element, you might be tempted to use a selector
like #myForm
select
. But, if another select is added later, it would be affected
as well. It would be better to use the name attribute to ensure that only this
select is chosen (as in #myForm select[name=day]
). Although
attribute-based selectors can be inefficient when used indiscriminately, in
this case there will only be a limited number of elements (hopefully just
one) actually inspected for their name.
Once you've made a selection, you'll often want to know whether you have anything to work with. You may be inclined to try something like:
if ($('div.foo')) { ... }
This won't work. When you make a selection using $()
, an object
is always returned, and objects always evaluate to true
. Even if
your selection doesn't contain any elements, the code inside the if
statement
will still run.
Instead, you should test the selection's length
property,
which tells you how many elements were selected. If the result is 0, the length
property
will be treated as false
when used as a boolean value.
if ($('div.foo').length) { ... }
Every time you make a selection, a lot of code runs, and jQuery doesn't do caching of selections for you. If you've made a selection that you might need to make again, you should save the selection in a variable rather than making the selection repeatedly.
var $divs = $('div');
Once you've stored your selection, you can call jQuery methods on the variable you stored it in just like you would have called them on the original selection.
In the above code, the variable name begins with a dollar sign. Unlike in other languages, there's nothing special about the dollar sign in JavaScript -- it's just another character. We use it here to indicate that the variable contains a jQuery object. This practice -- a sort of Hungarian notation -- is merely convention, and is not mandatory.
A selection only fetches the elements that are on the page when you make the selection. If you add elements to the page later, you'll have to repeat the selection or otherwise add them to the selection stored in the variable. Stored selections don't magically update when the DOM changes.
jQuery has many methods that you can use to operate on selections, There are methods to refine selections by filtering or selecting descendant elements, traverse to neighboring elements or parent elements, and read/write values of various properties.
Although $ is a jQuery function, remember that functions are also objects, so that a function can contain properties and methods. jQuery adds properties and methods to $ after it is created. In particular, the code that loads jQuery defines a property, fn, or the $ object. The fn object, in turn, contains numerous methods, like the addClass method that we have already seen.
Sometimes you have a selection that contains more than what you're after; in this case, you may want to refine your selection. jQuery offers several methods for zeroing in on exactly what you're looking for.
The $ function can accept an optional second parameter, context, that is a jQuery object to use as a starting point for the selection process. This context provides a means to limit the search within a specific node or a jQuery object containing a set of nodes. This is great when you have a very large DOM tree and need to find, for example, all the a
tags within a specific part of the DOM tree.
Let's say we wanted to remove all the nested tables in the DOM, here's what we could do:
/* get all 'table' tags and put them in a variable called $tables. $tables now becomes a "jQuery object". */ var $tables = $('table'); //outputs the # of tables in the DOM console.log($tables.length); /* get the inner tables in $('table', $tables), notice the 2nd argument, $tables, which is the "context" in which jQuery is making the selection. */ var $innerTables = $('table', $tables); //filter out, using the "not" operation, all the inner tables from $tables $tables = $tables.not($innerTables); //now prints the # of tables without the inner tables console.log($tables.length);
Now, in finding the inner tables to remove, the parameter to the "not" operation is the selection of all 'table' tags within the elements in the $tables object itself (in other words, within all the 'table' elements already selected). Therefore, $tables is the context in the case.
In pure JavaScript terms, the engine is going to perform getElementsByTagName('table') on each element in $tables, and return those elements so that they can be used in the "not" method. This approach becomes useful when you have a collection that you have refined to a point where it would no longer be easy or efficient to specify it with a single selector string, and you then want to use it as a context for further selection.
Most of the refining methods accept a selector as a parameter:
method( selector )
The selector is a string containing a selector expression
to match the current set of elements against. By default, for most of the refining
operations, the selector string operates in the context of the document,
that is, the selector is evaluated from the root level, not the level reached
by the selection. So, for example, using the not function to exclude
a subset of elements, $('table').not('table')
will not remove
nested tables. It will result in an empty set, since the 'table' selector
for the not operation will be applied from the root of the document,
not from the level reached by the original query, and we will remove exactly
the same elements we originally found.
The find method, for example, uses the current collection as the context,
so that the selector string is evaluated using the current collection as the
starting point. So, $('ul').find('ul')
will find only inner lists.
Some of the refining/filtering functions accept additional forms of the parameter list. The full set of overload possibilities is below.
method( function(index) )
A function used as a test for each element in the set. this is the current DOM element. If the function returns true, the element is included in the result collection.
method( element(s) )
An element or array of elements to match the current collection against. The matching is done using an equality test. The result will only include items that are in the set of elements, in effect producing the intersection of the filtered current collection with the set of elements.
method( jQuery object )
An existing jQuery object to match the current set of elements against. Similar to using elements, but using jQuery collection instead, again producing the intersection of the filtered current collection with passed-in collection.
Method | Description | Parameters |
---|---|---|
filter | filters the collection to only include elements that match the passed-in selector | selector, function, elements, jQuery |
find | finds descendant elements that match the parameter; uses the current collection as the context for a selector | selector, elements, jQuery |
children | finds all children, optionally filtered by a selector | none, selector |
first | finds the first element in the collection | none |
last | finds the last element in the collection | none |
eq(n) | finds the nth element in the collection | index |
is | tests the current collection and returns true if any element in it matches the selector (technically speaking, this isn't a filtering method, but that is the category that jQuery's documentation lists it under) | selector, function, elements, jQuery |
has | finds all elements in the current collection that have at least one descendant that matches the selector | selector, elements |
not | returns a new collection containg all the original elements except elements that match the selector | selector, function, elements, jQuery |
The original selection is not modified by the operation, but a new, modified set is returned. For example, if we wanted to find li elements and eliminate those with the special class, we could do:
var $items = $('li'); var $notSpecialIitems = $items.not('.special');
The $items collection is unchanged by the not operation -- a new collection is created and returned. If we wanted to use just one variable, we would need to do the following (recognizing that in this case it would be simpler to just use a single chained statement instead of two separate statements, but that would not make this concept clear):
var $items = $('li'); $items = $items.not('.special');
---- C O D E O M I T T E D ---- <script> $(function() { // div.foo elements that contain <p>'s $('div.foo').has('p').addClass('big'); // h1 elements that don't have a class of bar $('h1').not('.bar').addClass('italic'); // unordered list items with class of current $('ul>li').filter('.current').addClass('redText'); // just the first unordered list item $('ul>li').first().addClass('yellowBg'); // the sixth $('ul>li').eq(5).addClass('italic'); }); </script> ---- C O D E O M I T T E D ---- </head> <body> <div class="foo">foo div w/o p</div> <div class="foo"><p>foo div w/ p</p></div> <h1 class="bar">h1 bar</h1> <h1>h1</h1> <ul> <li class="current">current and the first</li> <li>not current</li> <li class="current">current</li> <li>not current</li> <li class="current">current</li> <li>not current, but the sixth</li> <li class="current">current</li> <li>not current</li> </ul> </body> </html>
If you call a method on a selection and that method returns a jQuery object, you can continue to call jQuery methods on the object.
$('#content').find('h3').eq(2).html('new text for the third h3!');
If you are writing a chain that includes several steps, you (and the person who comes after you) may find your code more readable if you break the chain over several lines.
$('#content') .find('h3') .eq(2) .html('new text for the third h3!');
If you change your selection in the midst of a chain, jQuery provides the $.fn.end method to get you back to your original selection.
<html> <head> <script src="../../jqy-libs/jquery.js"></script> <script> $(function() { $('#content') .find('h3') .eq(2) .html('new text for the third h3!') .end() // restores the selection to all h3's in #content .eq(0) .html('new text for the first h3!'); }); </script> </head> <body id="content"> <h3>First</h3> <h3>Second</h3> <h3>Third</h3> <h3>Fourth</h3> </body> </html>
Open the file jqy-concepts/Exercises/index.html in your browser. Use jqy-concepts/Exercises/js/sandbox.js to accomplish the following:
div
elements that have a class of 'module'
.
Add the 'showMe'
class to them. #myList
unordered
list. Try each of them using the 'showMe2'
class. Which is
the best to use? Why?'showMe2'
class to it..length
).
Display in the console.alt
attribute.
Display in the console.'oddRow'
class
to them.$( function() { // Select all of the div elements that have a class of 'module' // Add the 'showMe' class to them. $('div.module').addClass('showMe'); // Three selectors to get the third item in the #myList $('#myListItem').addClass('showMe2'); // above is best -- IDs are always the fastest selector // but, would it always be the third item? $('#myList>li:eq(2)').addClass('showMe2'); $('#myList>li').eq(2).addClass('showMe2'); // either would be best if the list item didn't have an ID $('#myList li:eq(2)').addClass('showMe2'); $('#myList li').eq(2).addClass('showMe2'); // not quite as good, since it would allow items from inner lists // Select the label for the search input using an attribute // selector. Add the 'showMe2' class to it. $('label[for=q]').addClass('showMe2'); // Figure out how many elements on the page are hidden console.log($(':hidden').length + " hidden"); // That includes head elements - below shows only elements in body, // and also omits script tags in body console.log($('body :hidden').not('script').length + " hidden"); // How many image elements on the page have an alt attribute? console.log($('img[alt]').length + " images with alt"); // Select all of the odd table rows in the table body. // Add the 'oddRow' class to them. $('#fruits>tbody>tr:odd').addClass('oddRow'); // if you don't specify tbody, you'll get the tr in the thead too } );
To test this solution, you will need to change the <script>
to point to sandbox-selecting.js instead of sandbox.js.
An id
selector is always the most efficient, since the browser can handle
that natively.
The label is tied to the input by the for
attribute, so we can use that
to find the label.
$('img[alt]')
will find any img tag where the alt attribute is present,
even though we didn't ask to find any specific value.
Starting the final query with an id
enables the jQuery engine to quickly
reduce the total number of tags it needs to examine.
Once you have a selection, you can call methods on the selection. Methods generally come in two different flavors: getters and setters. Getters return a property of the first selected element; setters set a property on all selected elements.
There are any number of ways you can change an existing element. Among the most common tasks you'll perform is changing the inner HTML or attribute of an element. jQuery offers simple, cross-browser methods for these sorts of manipulations. You can also get information about elements using many of the same methods in their getter incarnations.
jQuery "overloads" its methods, so the method used to set a value generally has the same name as the method used to get a value. When a method is used to set a value, it is called a setter. When a method is used to get (or read) a value, it is called a getter.
The getter methods return a single value from the first element in the collection, while the setters affect each element in the collection. (The lone exception is the text() method; as a getter, it returns the concatenation of the text content of all the elements in the collection.)
$('h1').html('hello world');
var content = $('h1').html();
Setters return a jQuery object, allowing you to continue to call jQuery methods on your selection; getters return whatever they were asked to get, meaning you cannot continue to call jQuery methods on the value returned by the getter.
Note: Changing things about elements is trivial, but remember that the change will affect all elements in the selection, so if you just want to change one element, be sure to specify that in your selection before calling a setter method.
<html> <head> <script src="../../jqy-libs/jquery.js"></script> <script> $(function() { $('#myDiv p:first') .html('New <strong>first</strong> paragraph!'); }); </script> </head> <body> <div id="myDiv"> <h1>Changing HTML</h1> <p>Original first paragraph.</p> <p>Second paragraph.</p> </div> </body> </html>
Try changing the html
method call to text
.
jQuery includes a handy way to get and set CSS properties of elements.
CSS properties that normally include a hyphen need to be camel cased in JavaScript. For example, the CSS property font-size is expressed as fontSize in JavaScript.
$('h1').css('fontSize'); // returns a string such as "19px"
// setting an individual property $('h1').css('fontSize', '100px'); // setting multiple properties $('h1').css({ 'fontSize' : '100px', 'color' : 'red' });
Note the style of the argument we use on the second line -- it is an object that contains multiple properties. This is a common way to pass multiple arguments to a function, and many jQuery setter methods accept objects to set multiple values at once.
As a getter, the $.fn.css method is valuable; however, it should generally be avoided as a setter in production-ready code, because you don't want presentational information in your JavaScript. Instead, write CSS rules for classes that describe the various visual states, and then simply change the class on the element you want to affect.
var $h1 = $('h1'); $h1.addClass('big'); $h1.removeClass('big'); $h1.toggleClass('big'); if ($h1.hasClass('big')) { ... }
Classes can also be useful for storing state information about an element, such as indicating that an element is selected.
jQuery offers a variety of methods for obtaining and modifying dimension and position information about an element.
The code in the example below is just a very brief overview of the dimensions functionality in jQuery; for complete details about jQuery dimension methods, visit http://api.jquery.com/category/dimensions/.
---- C O D E O M I T T E D ---- $(function() { var msg = ""; // set the width of all H1 elements $('h1').width('300px'); // gets the width of the first H1 msg += "Width: " + $('h1').width() + ", "; // sets the height of all H1 elements $('h1').height('150px'); // gets the height of the first H1 msg += "Height: " + $('h1').height() + "\n"; // returns an object containing position // information for the first H1 relative to // its "offset (positioned) parent" msg += "Top: " + $('h1').position().top + ", " + "Left: " + $('h1').position().left; alert(msg); }); ---- C O D E O M I T T E D ---- </html>
An element's attributes can contain useful information for your application, so it's important to be able to get and set them.
The $.fn.attr method acts as both a getter and a setter. As with the $.fn.css method, $.fn.attr as a setter can accept either a key and a value, or an object containing one or more key/value pairs.
// returns the href for the first a element in the document $('a').attr('href');
The attr method will change an attribute or property of a selection.
$('#myDiv a:first').attr('href', 'newDestination.html');
The attr method can accept an object instead of two separate parameters -- all of the object's properties will be used to set corresponding properties in the selection.
$('#myDiv a:first').attr({ href : 'newDestination.html', rel : 'super-special' }).html('New Destination');
You can also specify a function for an attribute's value -- the function will be invoked for each element in the collection. It can examine the tag and calculate a value from the tag'a properties.
$('#myDiv a:last').attr({ rel : 'super-special', href : function() { return 'new/' + $(this).attr('href'); } }.html('new/Destination'));
<html> <head> <script src="../../jqy-libs/jquery.js"></script> <script> $(function() { $('a').attr({ 'title' : 'all titles are the same too!', 'href' : 'allMyHrefsAreTheSameNow.html' }); $('a:first').attr('href', 'somethingNew.html'); }); </script> </head> <body> <a href="#">Home</a> <a href="#">Top</a> <a href="#">Up</a> </body> </html>
This time, we broke the object up into multiple lines. Remember, whitespace doesn't matter in JavaScript, so you should feel free to use it liberally to make your code more legible! You can use a minification tool later to strip out unnecessary whitespace for production.
---- C O D E O M I T T E D ---- <script> $(function() { // Manipulating single attributes $('#myDiv a:first').attr('href', 'newDestination.html'); // Manipulating multiple attributes $('#myDiv a:first').attr({ href : 'newDestination.html', rel : 'super-special' }).html('New Destination'); // Manipulating attributes using a function $('#myDiv a:last').attr({ rel : 'super-special', href : function() { return 'new/' + $(this).attr('href'); } }).html('new/Destination'); // just to verify that the rel attribute got set $('#myDiv [rel=super-special]').addClass("redText"); }); </script> </head> <body> <div id="myDiv"> <a target="popup" href="#">Old destination (here).</a> <a target="popup" href="#">Old destination (here).</a> <a target="popup" href="#">Old destination (here).</a> </div> </body> </html>
jQuery provides $.fn.removeAttr(attributeName) for this purpose.
While these will be covered in more detail later, the show() and hide() methods do what you might expect -- show or hide the selected elements.
If you have a complex task to perform on your selection, you can iterate over its elements using $.fn.each. The function that you pass to this method is run individually for all of the elements in a selection. The function receives the index of the current element and the DOM element itself as arguments. Inside the function, the DOM element is also available as this. (The DOM element is not a jQuery object, though -- it is a plain JavaScript element.)
<html> <head> <script src="../../jqy-libs/jquery.js"></script> <script src="../../jqy-libs/fix-console.js"></script> <script> $(function() { $('#myList li').each(function(idx, el) { console.log( 'Element ' + idx + ' has the following html: ' + $(el).html() ); }); }); </script> </head> <body> <ul id="myList"> <li>A</li> <li class="selected">B</li> <li>C</li> <li>D</li> <li>E</li> </ul> </body> </html>
Once you have a jQuery selection, you can find other elements using your selection as a starting point.
For complete documentation of jQuery traversal methods, visit https://api.jquery.com/category/traversing.
Be cautious with traversing long distances in your documents -- complex traversal makes it imperative that your document's structure remain the same, something that's difficult to guarantee even if you're the one creating the whole application from server to client. One or two-step traversal is fine, but you generally want to avoid traversals that take you from one container to another.
A useful subset of jQuery's traversal methods is listed below. For most of
them, the only parameter choices are none or an optional selector that is used
to filter the set of elements returned. For example, the next() method
returns the element after the current element. next(selector) returns
the element that follows the current element only if it matches the selector.
In other words, next('p')
doesn't
return the next p
tag -- it returns the next element if and only if it is a p
tag.
Method | Description | Parameters |
---|---|---|
find | finds descendant elements that match the parameter; uses the current collection as the context for a selector | selector, elements, jQuery |
children | finds all children, optionally filtered by a selector | none, selector |
parent | finds the parent element of each element in the current set | none, selector |
offsetParent | finds first positioned ancestor of each element in the current set | none |
closest | finds the closest ancestor element of each element in the current set | none, selector |
next | finds the next sibling of each element in the current set | none, selector |
prev | finds the previous sibling of each element in the current set | none, selector |
nextAll | finds all following siblings of each element in the current set | none, selector |
prevAll | finds all preceding sibling of each element in the current set | none, selector |
siblings | finds all the siblings of each element in the current set | none, selector |
---- C O D E O M I T T E D ---- $(function() { $('h1').next('p').addClass('redText'); $('input[name=first_name]').closest('form').addClass('yellowBg'); $('input[name=first_name]').parent().addClass('redText'); $('#myList').children().addClass('redText'); // below doesn't work as expected if more than // one element is selected $('li.selected').siblings().addClass('italic'); // but this does work as expected $('li:not(.selected)').addClass('blueText'); }); </script> ---- C O D E O M I T T E D ---- </head> <body> <h1>Heading</h1> <p>A paragraph (will be red)</p> <p>Another paragraph (won't be red)</p> <h1>Heading</h1> <h3>A heading 3</h3> <p>Another paragraph (won't be red)</p> <form> <label>First name: <input type="text" name="first_name" /></label> <label>Last name: <input type="text" name="last_name" /></label> </form> <ul id="myList"> <li>A</li> <li class="selected">B</li> <li>C</li> <li>D</li> <li>E</li> </ul> </body> </html>
Open index.html again in your browser. Use sandbox.js to accomplish the following:
alt
attribute.'showMe'
class to the form.#myList
that has a class of 'current'
and
remove that class from it; add a class of 'current'
to
the next list item.select
element inside #specials
;
traverse your way to the submit button. Set its disabled
attribute to true
.#slideshow
element; add
the class 'current'
to it, and then add a class of 'disabled'
to
its sibling elements. $( function() { // Get all the image elements on the page; log each image's // alt attribute. $('img').each(function(i) { console.log($(this).attr('alt')); //console.log(this.alt); }); // Get the search input text box, then traverse up to the form // and add a class to the form that contains it. $('input[name="q"]').closest('form').addClass('showMe'); // Get the list item inside #myList that has a class of 'current' // and remove that class from it; add a class of 'current' // to the next list item. $('#myList li.current') .removeClass('current') .next() .addClass('current'); // Get the select element inside #specials; traverse your way to // the submit button. Set its disabled attribute to true. $('#specials select') .parent() // Could also use closest('form') .next() // instead of these two lines .find('input.input_submit') .attr('disabled', true); // Get the first list item in the #slideshow element; add the class // 'current' to it, and then add a class of 'disabled' to its // sibling elements. $('#slideshow li:first') .addClass('current') .siblings() .addClass('disabled'); } );
To test this solution, you will need to change the <script>
to point to sandbox-traversing.js instead of sandbox.js.
Once you've made a selection, the fun begins. You can change, move, remove, and clone elements. You can also create new elements via a simple syntax.
For complete documentation of jQuery manipulation methods, visit https://api.jquery.com/category/manipulation.
There are a variety of ways to move elements around the DOM; generally, there are two approaches:
For example, jQuery provides $.fn.insertAfter
and $.fn.after
.
The $.fn.insertAfter
method places the selected element(s) after
the element that you provide as an argument; the $.fn.after
method
places the element provided as an argument after the selected element. Several
other methods follow this pattern: $.fn.insertBefore
and
$.fn.before
; $.fn.appendTo
and $.fn.append
;
and $.fn.prependTo
and $.fn.prepend
.
As a way to remember which is which, consider the following. Assuming that
$newItem is the item we are placing into the page, and $destination is the
element relative to which we are placing $newItem
:
$newItem.appendTo($destination); $newItem.prependTo($destination); $newItem.insertBefore($destination); $newItem.insertAfter($destination); $destination.append($newItem); $destination.prepend($newItem); $destination.before($newItem); $destination.after($newItem);
The method that makes the most sense for you will depend on what elements
you already have selected, and whether you will need to store a reference to
the elements you're adding to the page. If you need to store a reference, you
will generally want to take the first approach -- placing the selected elements
relative to another element -- as it returns the element(s) you're placing.
In this case, $.fn.insertAfter
, $.fn.insertBefore
, $.fn.appendTo
,
and $.fn.prependTo
will be your tools of choice.
---- C O D E O M I T T E D ---- <script> $(document).ready(function() { // make the first list item the last list item $('#myList li:first').appendTo('#myList').addClass('current'); // another approach to the same problem $('#myList').append($('#myList li:first')).addClass('italic'); // note that there's no way to access the // list item that we moved, as this returns // the list itself }); </script> ---- C O D E O M I T T E D ---- </html>
When you use methods such as $.fn.appendTo
, you are moving the
element; sometimes you want to make a copy of the element instead. In this
case, you'll need to use $.fn.clone
first. The clone method is
overloaded -- with no parameters, it makes a shallow clone, which does not
clone event handlers and data (jQuery has a facility for associating data with
an element, which we will cover later). If a true value is passed as a parameter,
event handlers and data are cloned as well.
Note: ids will get cloned as well as other attributes, but you shouldn't have two page elements with the same id. So, after cloning an element with an id, you should either remove the id or set it to some unique value, before inserting it into the document.
$('#myDiv') .clone().removeAttr('id') .appendTo('body'); // or $('#myDiv') .clone().attr('id', 'newId_1') .appendTo('body');
// copy the first list item to the end of the list $('#myList li:first').clone().appendTo('#myList');
There are two ways to remove elements from the page: $.fn.remove
and $.fn.detach
.
You'll use $.fn.remove
when you want to permanently remove the
selection from the page; while the method does return the removed element(s),
those elements will not have their associated data and events attached to them
if you return them to the page.
If you need the data and events to persist, you'll want to use $.fn.detach
instead.
Like $.fn.remove
, it returns the selection, but it also maintains
the data and events associated with the selection, so you can restore the selection
to the page at a later time.
Note: The $.fn.detach
method is extremely valuable
if you are doing heavy manipulation to an element. In that case, it's beneficial
to $.fn.detach
the element from the page, work on it in your code,
and then restore it to the page when you're done. This saves you from expensive "DOM
touches" while maintaining the element's data and events.
If you want to leave the element on the page but simply want to remove its
contents, you can use $.fn.empty
to dispose of the element's inner
HTML.
jQuery offers a trivial and elegant way to create new elements using the
same $()
method you use to make selections.
If you create a string of text with the HTML for an element, then pass it
to $()
, a DOM element will be created. The method returns a single-element
collection containing the new element. You can then use any of the methods
discussed earlier to add it to the document.
You can also pass an HTML string to any of the methods that accept an element to insert into the DOM.
$('<p>This is a new paragraph!</p>').prependTo($('body')); $('<li class="new">new list item</li>').appendTo($('#myList')); $('body').append('<p>This is another new paragraph!</p>');
The above approaches will also work if you use a selector for the destination, instead of a jQuery object. The difference in the code below is that the parameters to appendTo and prependTo are simple strings, instead of return values from the $ function.
$('<p>This is a new paragraph!</p>').prependTo('body'); $('<li class="new">new list item</li>').appendTo('#myList');
You can pass a second parameter with options representing body content and attributes for the tag.
$('<a/>', { html : 'This is a <em>new</em> link', 'class' : 'new', href : 'foo.html' }).appendTo($('body'));
Note that the 'class' property of the element attributes object is quoted -- this is to avoid conflicts with the reserved word class.
Also note the empty <a />
tag -- it gets populated by
the html property of the second parameter.
It is worth noting that this approach of creating an element with an
attributes object only works when the HTML and attributes are passed to the
$
function. You cannot pass both an HTML string and an attributes
object to the methods like append. For string parameters, it will
only accept a single parameter with the HTML string, or a series of strings,
each representing an element to create and add. But, you could instead pass
it a jQuery object, including one you created from a string.
// Won't work $('#myDiv') .append('<h2/>', { html: 'New Heading', 'class': 'yellowBg' }); // Will work $('#myDiv') .append($('<h2/>', { html: 'New Heading', 'class': 'yellowBg' }));
---- C O D E O M I T T E D ---- <script> $(function() { // simple elements $('<p>A new paragraph!</p><p>Another new paragraph!</p>') .prependTo($('body')); $('<li class="new">new list item</li>').appendTo($('#myList')); $('body').append('<p>This is another new paragraph!</p>'); // element with attributes object $('<a/>', { html : 'This is a <em>new</em> link', 'class' : 'new', href : 'foo.html' }).appendTo($('body')); }); </script> </head> <body> <ul id="myList"> <li>First</li> <li>Second</li> <li>Third</li> <li>Fourth</li> </ul> <hr /> </body> </html>
The syntax for adding new elements to the page is so easy, it's tempting to forget that there's a huge performance cost for adding to the DOM repeatedly. If you are adding many elements to the same container, you'll want to concatenate all the HTML into a single string, and then append that string to the container instead of appending the elements one at a time. You can use an array to gather all the pieces together, then join them into a single string for appending.
var myItems = [], $myList = $('#myList'); for (var i=0; i<100; i++) { myItems.push('<li>item ' + i + '</li>'); } $myList.append(myItems.join(''));
Open the file index.html in your browser. Use the sandbox.js or work in Google Chrome DevTools to accomplish the following:
#myList
.
(Hint: for (var i = 0; i<5; i++) { ... }
)h2
and another paragraph to the last div.module
.option
to the select
element; give the option the value "wednesday",
and inner HTML of "Wednesday".div.module
to the page after the last one; put
a copy of one of the existing images inside of it. Note that the images
have ids, which should be removed from the copy. $( function() { // Add five new list items to the end of #myList. for (var i = 0; i < 5; i++) { $('<li/>', { html: 'new item ' + i, 'class': 'current' }) .appendTo('#myList'); } // Remove the odd list items from #myList. $('#myList>li:odd').remove(); // Add another h2 and another paragraph to the last div.module. $('div.module:last') .append('<h2>New Heading</h2>').append('<p>New Paragraph</p>'); // Add another option to the select element; // give the option the value "wednesday", // and inner HTML of "Wednesday". $('#specials select[name=day]') .append('<option value="wednesday">Wednesday</option>'); // another way $('<option>Thursday</option>') .val('thursday') .appendTo('#specials select[name=day]'); // Add a new div.module to the page after the last one; // put a copy of one of the existing images inside of it. // Note that the images have ids, which should be removed. $('<div class="module">') .append($('#slideshow img:first').clone().removeAttr('id')) .insertAfter('div.module:last'); } );
To test this solution, you will need to change the <script>
to point to sandbox-manipulating.js instead of sandbox.js.