Honing the contents of a set

You saw that it’s a simple matter to augment a jQuery object from multiple selectors chained together with the add() method. It’s also possible to chain selectors together to form an except relationship by employing the not() method. This is similar to the :not filter selector we discussed in the previous lesson, but it can be employed in a similar fashion to the add() method to remove elements from the set anywhere within a jQuery chain of methods.

Let’s say that you want to select all img elements in a page that have a title attribute except for those that contain the text “puppy” as their value. You could come up with a single selector that expresses this condition (namely img[title]:not ([title*="puppy"])), but for the sake of illustration, let’s pretend that you forgot about the :not filter. By using the not() method, which removes any elements from a set that match the specified selector, you can express an except type of relationship. To perform the described match, you can write

$('img[title]').not('[title*="puppy"]');

Type this expression into the jQuery Operations Lab Page and execute it. You’ll see that only the first dog image has the highlight applied. The black puppy, which is included in the original set because it possesses a title attribute, is removed by the not() invocation because its title contains the text “puppy”.

Method syntax: not
not(selector)
Creates a copy of the set without the elements that match the criteria specified by the value of the selector parameter.
Parameters
selector(Selector|Element|Array|jQuery|Function) Specifies which elements are to be removed. If the parameter is a jQuery selector, the matching elements are removed. If an element reference, array of elements, or a jQuery set is passed, those elements are removed from the set. If a function is passed, the function is invoked for each item in the set (with this set to the item), and returning true from the invocation causes the item to be removed from the set. In addition, jQuery passes the index of the element inside the set as the first argument of the function, and the current element as the second argument.
Returns
A copy of the original set without the removed elements.

The not() method can be used to remove individual elements from the set by passing a reference to an element or an array of element references. The latter is interesting and powerful because, as you’ll remember, any jQuery set can be used as an array of element references.

When maximum flexibility is needed, you can pass a function to not() and make a determination of whether to keep or remove the element on an element-by-element basis. jQuery passes to the function an argument that specifies the index of the element inside the set. Consider this example:

$('div').not(function(index) {
  return $(this).children().length > 2 && index % 2 === 0;
});

This statement will select all <div>s of the page and then remove those that have more than two children and have an odd index (not position) inside the set. If you want to see a live result, you can try it in the Lab Page and you’ll receive four matches.

This method allows you to filter the set in ways that are difficult or impossible to express with a selector expression by resorting to programmatic filtering of the set elements.

For those times when the test applied within the function passed to not() seems to be the opposite of what you want to express, not() has an inverse method, filter(). It works in a similar fashion, except that it removes elements when the function returns false.

For example, let’s say that you want to create a set of all <td>s that contain a positive integer value. For such situations, you can employ the filter() method, as follows:

$('td').filter(function() {
  return this.innerHTML.match(/^\d+$/);
});

This statement creates a set of all td elements and then invokes the function passed to the filter() method for each of them, with the current matched element as the this value for the invocation. The function used employs a regular expression to determine whether the element content matches the described pattern (a sequence of one or more digits), returning null if not. Elements whose filter function invocation returns false, or a falsy value in general (nullundefined, and so on), aren’t included in the returned set.

The syntax of the filter() method is the following.

Method syntax: filter
filter(selector)
Creates a copy of the set and removes elements from the new set that don’t match the criteria specified by the value of the selector parameter.
Parameters
selector(Selector|Element|Array|jQuery|Function) Specifies which elements are to be removed. If the parameter is a string containing a selector, any elements that don’t match are removed. If an element reference, array of elements, or jQuery object is passed, all but those elements are removed from the set. If a function is passed, the function is invoked for each element in the set (with this referencing the current element), and returning false from the invocation causes the element to be removed from the set. In addition, jQuery passes the index of the element inside the set as the first argument of the function and the current element as the second argument.
Returns
A copy of the original set without the removed elements.

Again, bring up the jQuery Operations Lab Page, type the previous expression in, and execute it. You’ll see that the table cells for the Invented column are the only td elements that end up being selected.

The filter() method can also be used with a selector expression. When used in this manner, it operates inversely to the corresponding not() method, removing any elements that don’t match the passed selector. This isn’t a super-powerful method, because it’s usually easier to use a more restrictive selector in the first place, but it can be useful within a chain of jQuery methods. Consider, for example,

$('img')
  .addClass('opaque')
  .filter('[title*="dog"]')
  .addClass('red-border');

This chained statement selects all images of a page, applies the opaque class to them, and then reduces the set to only those image elements whose title attribute contains the string dog before applying another class named red-border. The result is that all the images end up semitransparent, but only the tan dog gets the red border treatment.

The not() and filter() methods give you powerful means to adjust a set of elements in a collection on the fly, based on almost any criteria concerning the elements in the set. But you can also subset the set, based on the position of the elements within the set. Let’s look at those methods next.

Obtaining subsets of a set

Sometimes you may wish to obtain a subset of a set based on the position of the elements within the set. jQuery provides a slice() method to do that. This method creates and returns a new set from any contiguous portion, or a slice, of an original set.

Method syntax: slice
slice(start[, end])
Creates and returns a new set containing a contiguous portion of the matched set.
Parameters
start(Number) The zero-based position of the first element to be included in the returned slice.
end(Number) The optional zero-based index of the first element not to be included in the returned slice, or one position beyond the last element to be included. If negative, it indicates an offset from the end of the set. If omitted, the slice extends to the end of the set.
Returns
The newly created set.

If you want to obtain a set that contains a single element from another set, based on its position in the original set, you can employ the slice() method. For example, to obtain the third element of a previous selection, you could write

$('img, div.wrapper', 'div').slice(2, 3);

This statement selects all the <img>s, and the <div>s having the class wrapper, within a <div>, and then generates a new set containing only the third element in the matched set. As you can see, your previously acquired knowledge comes back again and again as you advance in the app.

Note that this is different from using get(2), which returns the third DOM element in the set, but is the same as using eq(2).

A statement such as

$('*').slice(0, 4);

selects all elements on the page and then creates a set containing the first four elements.

To grab elements up to the end of the set, the statement

$('*').slice(4);

matches all elements on the page and then returns a set containing all but the first four elements.

Another method you can use to obtain a subset of a set is has(). Like the :has filter, this method tests the children of the elements in a jQuery object, using this check to choose the elements to become part of the subset.

Method syntax: has
has(selector)
Creates and returns a new set containing only elements from the original set that contain descendants that match the passed selector expression.
Parameters
selector(Selector|Element) A string containing a selector to be applied to all descendants of the elements in the set, or a DOM element to be tested. Only elements within the set possessing an element that matches the selector, or the passed element, are included in the returned set.
Returns
A jQuery object.

For example, consider this line:

$('div').has('img[alt]');

This expression will create a set of all <div>s and then create and return a second set that contains only those <div>s that contain at least one descendant <img> that possesses an alt attribute.

Translating elements of a set

Often you’ll want to perform transformations on the elements of a set. For example, you may want to collect all the IDs of the elements in the set or perhaps collect the values of a set of form elements in order to create a query string from them. The map() method comes in handy for such occasions.

Method syntax: map
map(callback)
Invokes the callback function for each element in the set and collects the returned values into a jQuery object.
Parameters
callback(Function) A callback function that’s invoked for each element in the set. Two parameters are passed to this function: the zero-based index of the element within the set and the element itself. The element is also established as the function context (the this keyword). To add an element into the new set, a value different from null or undefined must be returned.
Returns
The set of translated values.

For example, the following code will collect all the IDs of all the <div>s on the page:

var $allIDs = $('div').map(function() {
  return this.id;
});

With this statement you’re actually retrieving a jQuery object containing the IDs, which is usually not what you want. If you want to work with a plain JavaScript array, you can append the toArray() method to the chain as such:

var allIDs = $('div').map(function() {
  return this.id;
})
.toArray();

There are other methods that we want to introduce in this lesson. Let’s discover more.

Traversing a set’s elements

The map() method is useful for iterating over the elements of a set in order to collect values or translate the elements in some other way, but you’ll have many occasions where you’ll want to iterate over the elements for more general purposes. For these occasions, the jQuery each() method is invaluable.

Method syntax: each
each(iterator)
Traverses all the elements in the matched set, invoking the passed iterator function for each of them.
Parameters
iterator(Function) A function called for each element in the matched set. Two parameters are passed to this function: the zero-based index of the element within the set and the element itself. The element is also established as the function context (the this reference).
Returns
The jQuery collection.

An example of using this method could be to set a property value on all elements in a matched set. For example, consider this:

$('img').each(function(i){
  this.alt = 'This is image[' + i + '] with an id of ' + this.id;
});

This statement will invoke the passed function for each img element on the page, modifying its alt property using the index of the element within the set and its ID.

So far you’ve seen a lot of methods you can use with a jQuery object, but you’re not finished yet! Let’s see more about how jQuery deals with them.


Posted

in

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *