Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery() objects can hold not-DOM objects?

Tags:

jquery

Analyzing the code showed by this SO question, I just noticed the way it's using jQuery to iterate a JSON array:

$(data).each(function() {

while in my mind an array should rather be iterated this way:

$.each(data, function() {

Indeed, the jQuery.each() manual page states:

The $.each() function is not the same as $(selector).each(), which is used to iterate, exclusively, over a jQuery object.

But since the OP seemed to have his code at least partially working I was curious to test, and discovered that it works!
Here is the proof:

var data = [
  {"key": "value-1"},
  {"key": "value-2"},
  {"key": "value-3"}
];

$(data).each(function() {
  document.write('<br />' + this.key);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

So if $(data).each() works when data is a JSON array, it seems to mean that this array is an acceptable content for $(data) to return a jQuery object.

Then pursuing the investigation I checked the jQuery(elementArray) manual page and looked at the jQuery( elementArray ) section, which states:

elementArray
Type: Array
An array containing a set of DOM elements to wrap in a jQuery object.

According with the above, an array of objects (instead of DOM elements) should fail.
So I tested to compare the objects returned by either this $(data) and a simple $('body'). Here is the result:

var data = [
  {"key": "value-1"},
  {"key": "value-2"},
  {"key": "value-3"}
];

function log(obj, init) {
  for (var prop in obj) {
    if (obj.hasOwnProperty(prop)) {
      var $row = $('tr[data-prop=' + prop + ']');
      if (!$row.length) {
        $row =
          $('<tr data-prop="' + prop + '"><th>' + prop + '</th></tr>')
          .appendTo($('table'));
        if (!init) {
          $row.append('<td></td>');
        }
      }
      $row.append('<td>' + JSON.stringify(obj[prop]).substr(0,25) + '</td>');
    }
  }
}

log($('body'), true);
log($(data), false);
table {
  border-collapse: collapse;
  border: 1px solid #000;
}
th, td {
  border: 1px solid #000;
  padding: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
  <tr>
    <th>Property</th>
    <th>$('body')</th>
    <th>$(data)</th>
  </tr>
</table>

Actually it appears that everything can be converted to a jQuery object.
I'm puzzled: am I reinventing the wheel!?

like image 973
cFreed Avatar asked Mar 17 '16 01:03

cFreed


2 Answers

You're reading into the documentation too much.

Yes, normally jQuery(elementArray) would be used in an array of elements. But, it can be an array of anything.

The intent is for you to use $.each() and possibly other convenience methods on arrays with normal elements. You'll find that other jQuery methods won't match anything in your array if your array doesn't have elements of some kind.

like image 157
Brad Avatar answered Oct 30 '22 06:10

Brad


Yes, jQuery constructor accepts an array and you can iterate through the returned jQuery collection using $.fn.each method and this within the handler refers to each element. A jQuery collection is an array-like object.

But, no, this doesn't mean that you can call all DOM related methods on the collection successfully. Just try $(data).html() as an example and you will get an error as it expects to see a DOM node and not a plain object or a string in the collection.

As another example try $(['foo']).text()and it throws Uncaught RangeError: Maximum call stack size exceeded error in jQuery 2.2.2.

And now try:

/**
 * `text` reads textContent of DOM elements (nodeType = 1)
 * and `nodeValue` of textNodes (nodeType = 3) 
 * and returns the concatenated text
 */
$([
  { "nodeType": 1, "textContent": "value-1" },
  { "nodeType": 3, "nodeValue": "value-2" },
  { "textContent": "I'm a rebel" }
]).text()

and it returns "value-1value-2"!

like image 23
undefined Avatar answered Oct 30 '22 05:10

undefined