|
| 1 | +--- |
| 2 | +level : beginner |
| 3 | +title : The jQuery Object |
| 4 | +attribution: Mike Pennisi |
| 5 | +github : jugglinmike |
| 6 | +--- |
| 7 | +When creating new elements (or selecting existing ones), jQuery returns the elements in a collection. |
| 8 | +Many developers new to jQuery assume that this collection is an array. |
| 9 | +It has a zero-indexed sequence of DOM elements, some familiar array functions, and a `length` property, after all. |
| 10 | +Actually, the jQuery object is more complicated than that. |
| 11 | + |
| 12 | +### What is a DOM element? What is the DOM, for that matter? |
| 13 | + |
| 14 | +The DOM (short for Document Object Model) is a representation of an HTML document. |
| 15 | +It may contain any number of DOM elements. |
| 16 | +At a high level, a DOM element can be thought of as a "piece" of a web page. |
| 17 | +It may contain text and/or other DOM elements. |
| 18 | +It is described by a type (i.e. "div", "a", "p", etc.) and any number of attributes (i.e. "src", "href", "class", etc.). |
| 19 | +For a more thorough description, please refer to [the official specification from the W3C](http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-745549614). |
| 20 | + |
| 21 | +Elements have properties like any JavaScript object. |
| 22 | +Among these properties are attributes like `tagName` and methods like `appendChild`. |
| 23 | +These properties are the only way to interact with the web page via JavaScript. |
| 24 | + |
| 25 | +### Why not just put the elements in an array? |
| 26 | + |
| 27 | +It turns out that working directly with DOM elements can be quite awkward. |
| 28 | +The jQuery object defines [a ton](http://api.jquery.com/) of methods to smooth out the experience for developers. |
| 29 | +For example: |
| 30 | + |
| 31 | +*Compatibility* |
| 32 | +The implementation of element methods varies across browser vendors and versions. |
| 33 | +The following snippet attempts to set the inner HTML of a `tr` element stored in `target`: |
| 34 | + |
| 35 | +<javascript caption="Setting the inner HTML with the native DOM API"> |
| 36 | +var target = document.getElementById("target"); |
| 37 | +target.innerHTML = "<td>Hello <b>World</b>!</td>"; |
| 38 | +</javascript> |
| 39 | + |
| 40 | +This works in many cases, but it will fail in most versions of Internet Explorer. |
| 41 | +In that case, the [recommended approach](http://www.quirksmode.org/dom/w3c_html.html) is to use pure DOM methods instead. |
| 42 | +By wrapping the `target` element in a jQuery object, these edge cases are taken care of, and the expected result is achieved in all supported browsers: |
| 43 | + |
| 44 | +<javascript caption="Setting the inner HTML with jQuery"> |
| 45 | +var target = document.getElementById("target"); |
| 46 | +$( target ).html( "<td>Hello <b>World</b>!</td>"); |
| 47 | +</javascript> |
| 48 | + |
| 49 | +*Convenience* |
| 50 | +There are also a lot of common DOM manipulation use cases that are awkward to accomplish with pure DOM methods. |
| 51 | +For instance, inserting an element stored in `newElement` after the `target` element requires a rather verbose DOM method: |
| 52 | + |
| 53 | +<javascript caption="Inserting a new element after another with the native DOM API"> |
| 54 | +var target = document.getElementById("target"); |
| 55 | +var newElement = document.createElement("div"); |
| 56 | +target.parentNode.insertBefore( target.nextSibling, newElement ) |
| 57 | +</javascript> |
| 58 | + |
| 59 | +By wrapping the `target` element in a jQuery object, the same task becomes much simpler: |
| 60 | + |
| 61 | +<javascript caption="Inserting a new element after another with jQuery"> |
| 62 | +var target = document.getElementById("target"); |
| 63 | +var newElement = document.createElement("div"); |
| 64 | +$( target ).after( newElement ); |
| 65 | +</javascript> |
| 66 | + |
| 67 | +For the most part, these details are simply "gotchas" standing between a developer and her goals. |
| 68 | + |
| 69 | +### Getting stuff in there |
| 70 | + |
| 71 | +When the jQuery function is invoked with a CSS selector, it will return a jQuery object wrapping any element(s) that match this selector. |
| 72 | +For instance, by writing |
| 73 | + |
| 74 | +<javascript caption="Selecting all 'h1' tags"> |
| 75 | +var allHeaders = $("h1"); |
| 76 | +</javascript> |
| 77 | + |
| 78 | +`headers` is now a jQuery element containing *all* the `<h1>` tags already on the page. |
| 79 | +This can be verified by inspecting the `length` property of `headers`: |
| 80 | + |
| 81 | +<javascript caption="Viewing the number of 'h1' tags on the page"> |
| 82 | +var allHeaders = $("h1"); |
| 83 | +alert( allHeaders.length ); |
| 84 | +</javascript> |
| 85 | + |
| 86 | +If the page has more than one `<h1>` tag, this number will be greater than one. |
| 87 | +Likewise, if the page has no `<h1>` tags, the `length` property will be zero. |
| 88 | +Checking the `length` property is a common way to ensure that the selector successfully matched one or more elements. |
| 89 | + |
| 90 | +If the goal is to select only the first header element, another step is required. |
| 91 | +There are a number of ways to accomplish this, the most straight-forward may be the `eq()` function. |
| 92 | + |
| 93 | +<javascript caption="Selecting only the first 'h1' element on the page (in a jQuery object)"> |
| 94 | +var headers = $("h1"); |
| 95 | +var firstHeader = headers.eq(0); |
| 96 | +</javascript> |
| 97 | + |
| 98 | +Now `firstHeader` is a jQuery object containing only the first `<h1>` element on the page. |
| 99 | +And because `firstHeader` is a jQuery object, it has useful methods like `html()` and `after()`. |
| 100 | +jQuery also has a method named `get()` which provides a related function. |
| 101 | +Instead of returning a jQuery-wrapped DOM element, it returns the DOM element itself. |
| 102 | + |
| 103 | +<javascript caption="Selecting only the first 'h1' element on the page"> |
| 104 | +var firstHeaderElem = $("h1").get(0); |
| 105 | +</javascript> |
| 106 | + |
| 107 | +Alternatively, because the jQuery object is "array-like", it supports array subscripting via brackets: |
| 108 | + |
| 109 | +<javascript caption="Selecting only the first 'h1' element on the page (alternate approach)"> |
| 110 | +var firstHeaderElem = $("h1")[0]; |
| 111 | +</javascript> |
| 112 | + |
| 113 | +In either case, `firstHeaderElem` contains the "native" DOM element. |
| 114 | +This means it has DOM properties like `innerHTML` and methods like `appendChild()`, but *not* jQuery methods like `html()` or `after()`. |
| 115 | +As discussed earlier, the element is more difficult to work with, but there are certain instances that require it. |
| 116 | +One such instance is making comparisons. |
| 117 | + |
| 118 | +### Not all jQuery objects are created `===` |
| 119 | + |
| 120 | +An important detail regarding this "wrapping" behavior is that each wrapped object is unique. |
| 121 | +This is true *even if the object was created with the same selector or contain references to the exact same DOM elements*. |
| 122 | + |
| 123 | +<javascript caption="Creating two jQuery objects for the same element"> |
| 124 | +var logo1 = $("#logo"); |
| 125 | +var logo2 = $("#logo"); |
| 126 | +</javascript> |
| 127 | + |
| 128 | +Although `logo1` and `logo2` are created in the same way (and wrap the same DOM element), they are not the same object. |
| 129 | +For example: |
| 130 | + |
| 131 | +<javascript caption="Comparing jQuery object"> |
| 132 | +alert( $("#logo") === $("#logo") ); // alerts 'false' |
| 133 | +</javascript> |
| 134 | + |
| 135 | +However, both objects contain the same DOM element. |
| 136 | +The `get` method is useful for testing if two jQuery objects have the same DOM element. |
| 137 | + |
| 138 | +<javascript caption="Comparing DOM elements"> |
| 139 | +var logo1 = $("$logo"); |
| 140 | +var logo1Elem = logo1.get(0); |
| 141 | + |
| 142 | +var logo2 = $("#logo"); |
| 143 | +var logo2Elem = logo2.get(0); |
| 144 | + |
| 145 | +alert( logo1Elem === logo2Elem ); // alerts 'true' |
| 146 | +</javascript> |
| 147 | + |
| 148 | +Many developers prefix a `$` to the name of variables that contain jQuery objects in order to help differentiate. |
| 149 | +There is nothing magic about this practice--it just helps some people to keep track of what different variables contain. |
| 150 | +The previous example could be re-written to follow this convention: |
| 151 | + |
| 152 | +<javascript caption="Comparing DOM elements (with more readable variable names)"> |
| 153 | +var $logo1 = $("#logo"); |
| 154 | +var logo1 = $logo1.get(0); |
| 155 | + |
| 156 | +var $logo2 = $("#logo"); |
| 157 | +var logo2 = $logo2.get(0); |
| 158 | + |
| 159 | +alert( logo1 === logo2 ); // alerts 'true' |
| 160 | +</javascript> |
| 161 | + |
| 162 | +This code functions identically to the example above, but it is a little more clear to read. |
| 163 | + |
| 164 | +Regardless of the naming convention used, it is very important to make the distinction between jQuery object and native DOM elements! |
| 165 | +Native DOM methods and properties are not present on the jQuery object, and vice versa. |
| 166 | +**Error messages like, "event.target.closest is not a function"' and "TypeError: Object [object Object] has no method 'setAttribute'" indicate the presence of this common mistake.** |
| 167 | + |
| 168 | +### jQuery objects are not "live" |
| 169 | + |
| 170 | +Given a jQuery object with all the paragraph elements on the page: |
| 171 | + |
| 172 | +<javascript caption="Selecting all 'p' elements on the page"> |
| 173 | +var allParagraphs = $("p"); |
| 174 | +</javascript> |
| 175 | + |
| 176 | +...one might expect that the contents will grow and shrink over time as `<p>` elements are added and removed from the document. |
| 177 | +This is how "nodelists" returned by the `getElementsByTagName` method work, after all. |
| 178 | + |
| 179 | +jQuery objects do **not** behave in this manner. |
| 180 | +The set of elements contained within a jQuery object will not change unless explicitly modified. |
| 181 | +This means that the collection is not "live"--it does not automatically update as the document changes. |
| 182 | +If the document may have changed since the creation the jQuery object, the collection should be updated by creating a new one! |
| 183 | +It can be as easy as re-running the same selector: |
| 184 | + |
| 185 | +<javascript caption="Updating the selection"> |
| 186 | +allParagraphs = $("p"); |
| 187 | +</javascript> |
| 188 | + |
| 189 | +### Wrapping up |
| 190 | + |
| 191 | +Although DOM elements provide all the functionality one needs to create interactive web pages, they can be a hassle to work with. |
| 192 | +The jQuery object wraps these elements to smooth out this experience and make common tasks easy. |
| 193 | +When creating or selecting elements with jQuery, the result will always be wrapped in a new jQuery object. |
| 194 | +If the situation calls for the native DOM elements, they may be accessed through the `get()` method and/or array-style subscripting. |
| 195 | + |
| 196 | +These distinctions may not be immediately obvious, but understanding them is an important step in fully utilizing jQuery as it was intended. |
0 commit comments