Using `push` on jQuery collection - javascript

jQuery collections are array-like Objects with a length property and methods from Array.prototype like splice, sort, and push. I'm aware that those methods are not chainable like typical jQuery methods, but the methods do work as expected.
Are there any reasons not to manipulate jQuery collections using those Array.prototype methods such as browser compatibility or issues with other jQuery methods?
Example
In a loop, add elements to a jQuery collection for later manipulation.
var $divs = $(); // Create empty jQuery collection.
for (i = 0; i < 8; i++) {
var div = document.createElement('div');
div.innerHTML = i+1;
$divs.push(div); // Add newly created div to collection
}
$divs.addClass('red').appendTo(document.body);
View on Codepen
Using push here instead of $divs = $divs.add(div); is more succinct and seems more efficient. If jsperf were working right now, I'm sure it would show better performance with push.

Here is .add from the jQuery source:
add: function( selector, context ) {
return this.pushStack(
jQuery.uniqueSort(
jQuery.merge( this.get(), jQuery( selector, context ) )
)
);
},
In this function 3 more methods are called... .merge, .uniqueSort (which is actually SizzleJS), and .pushStack...
In addition to just .push, jQuery will:
Merge your input if it's an array of selectors. (.merge)
Remove any duplicate entries. (.uniqueSort)
Accept flexible input with DOM elements (.pushStack)
So, to conclude that, if you're worried about input, .add is good to use, since jQuery makes it flexible (regular DOM or jQuery objects work), but it also removes duplicate entries and can take in multiple items with an array.

You can pass an Array into jQuery, this means you can make full use of a native Array
// build
var j, el, ly = [];
for (j = 0; j < 3; ++j) {
el = document.createElement('span');
ly.push(el);
}
// wrap
var jq = $(ly); // jQuery object [<span>, <span>, <span>]
Adding more elements can be done with pushStack and another Array
jq.pushStack([document.createElement('div')]);
jq; // [<span>, <span>, <span>, <div>]
Or more generally using add which accepts any input the normal jQuery constructor takes
jq.add(document.createElement('hr'));
jq; // [<span>, <span>, <span>, <div>, <hr>]

Related

JavaScript Web APIs vs jQuery for DOM selection/manipulation

What are the best practices regarding DOM traversal/selection/manipulation? If I'm targeting the same group of elements more than once to do something, should I stick to only one way of selecting them?
For example:
var slidesHTML = document.getElementsByClassName("slide"); //HTMLCollection
var slidesNODE = document.querySelectorAll("slide"); //NodeList
var slidesJQUERY = $(".slide"); //jQuery object
Right now, I'm targeting these elements when I want to call a jQuery method and targeting them again when I want to call a NodeList method, and again when I want to call a HTMLCollection method etc.
This seems redundant, which feels like bad practice.
However, if I select group of elements once I will have to rewrite all the other methods I want to call from scratch, which seems like overkill.
While a better approach might be to stick to one way of selecting them, the lack of flexibility might not work well for you.
You could instead just write an agnostic element getter to get the elements and return them in the format you want:
var getElements = function getElements(selector, format) {
var elements;
if (format.toLowerCase() === "htmlcollection") {
elements = document.getElementsByClassName(selector); //HTMLCollection
}
if (format.toLowerCase() === "nodelist") {
elements = document.querySelectorAll(selector); //NodeList
}
if (format.toLowerCase() === "jquery") {
elements = $(selector); //jQuery object
}
return elements;
};
...
// meanwhile, in another part of the code...
var slidesHTML = getElements("slide", "HTMLCollection");
var slidesNODE = getElements(".slide", "NodeList");
var slidesJQUERY = getElements(".slide", "jQuery");
Personally speaking, I find the HTMLCollection and NodeList collections to be of little value in most cases, so for projects where I'm not using jQuery (or a similar library), I just use the following:
var elements = Array.prototype.slice.call(document.querySelectorAll(selector));
This approach allows me the flexibility (and specificity) of CSS Selector Syntax to get DOM elements and pulls them back into an Array of elements where it is trivial to iterate over them or perform other operations to them.

jQuery append() for multiple elements after for loop without flattening to HTML

I have a loop:
for (index = 0; index < total_groups; index += 1) {
groups[index].list_item = $(list_item_snippet);
// Closure to bind the index for event handling
(function (new_index) {
groups[index].list_item.find('.listing-group-title')
.html(groups[index].Group.Name)
.click(function(e){
fns.manageActiveGroup(new_index, groups);
return false;
});
})(index);
// Append to DOM
mkp.$group_listing.append(groups[index].list_item);
};
I would rather not call append() each time the loop fires.
I know that I could use a String and concatenate the markup with each loop iteration and append the string to mkp.$group_listing at the end, however this flattens the object and the bindings are lost (I am not relying on IDs).
Is there a way to perhaps add my objects to an array and append them all in one go at the bottom without flatening to HTML?
Assumptions:
$(list_item_snippet) contains some HTML defining a list item (and includes an element with class .listing-group-title).
groups is a block of JSON defining a 'group' in my script
The closure works perfectly
Edit:
Found that I can use the following syntax to append multiple elements:
mkp.$group_listing.append(groups[0].list_item, groups[1].list_item );
But i obviously need to automate it - it's not an array it's just optional additional function parameters so I'm not sure how to do this.
To append an array of elements to a selector you can use this:
$.fn.append.apply($sel, myArray);
In your case, since it's actually the .list_item property of each array element that you need you can use $.map to extract those first:
$.fn.append.apply(mkp.$group_listing, $.map(groups, function(value) {
return value.list_item;
}));
Instead of bind it the way you've done, if you bind it using on() like below,
$(document).on('click', '.listing-group-title', function() {
// click handler code here
});
You can flatten the HTML and append it in one statement and it'll still work.
Note: For better efficiency, replace document in the above statement to a selector matching the closest parent of .listing-group-title
Yes. Use the jQuery add method to add all your items to a jQuery object. Then append that one object.
http://api.jquery.com/add/
EDIT: Example:
var arr = $();
for (index = 0; index < total_groups; index += 1) {
groups[index].list_item = $(list_item_snippet);
// Closure to bind the index for event handling
(function (new_index) {
...
})(index);
// Add to jQuery object.
arr.add(groups[index].list_item));
};
mkp.$group_listing.append(arr);

JQuery collection array suddenly becomes DOM HTMLDivElement

In my code I have a function that accepts an array of JQuery collections and applies some code to each via a for-loop.
The problem is that as soon as I reference one it somehow becomes a HTMLDivElement instead of a collection object.
function vacant_now($timetables, now){
console.log("1:" + $timetables);//still fine
for (var i=0; i < $timetables.length; i++){
console.log("2:" + $timetables[i]);//problem is here
var $timetable = $timetables[i];
console.log("3:" + $timetable);
$timetable.find(".booking").each(function(){ ...
it's called like this:
vacant_now($page.find(".timetable"), now);
I'm stumped.
The jQuery collection array is an array of DOM elements.
Doing this: $('#myDiv')[0];
returns the same as: document.getElementByID('myDiv');
Solution:
Use $('.timetable').eq(index);
for (var i=0; i < $timetables.length; i++){
$timetables.eq(i);
}
Cheers
Using [i] on a jQuery object returns the dom element. If you want the jQuery object at a specific index use the .eq() function:
console.log("2:" + $timetables.eq(i));
Example - http://jsfiddle.net/8FeEf/1/
With jQuery you can use the .each method, and after you must 'jquerify' the object :
function vacant_now($timetables, now) {
$timetables.each(function() {
var $timetable = $(this);
});
}
A jquery collection is in fact an array of DOM elements.
You can also use a for, the syntaxe is a little bit more verbose.
In all case, you must jquerify the object.
Exemple : http://jsfiddle.net/FTcpD/

Convert NodeList to array

I'm having a hard time converting a NodeList to an array in IE 8. The following works perfectly in Chrome, but in IE 8 toArray() is not recognized as valid:
NodeList.prototype.toArray = function() {
var a = [];
for (var i = 0, len = this.length; i < len; i++) {
a[i] = this[i];
}
return a;
}
document.all.tags("div").toArray();
I tried adding a prototype function to an array just to check my sanity and it works correctly. That makes me think IE 8 doesn't actually return a NodeList? Here's a full example:
http://jsfiddle.net/e4RbH/
What am I doing wrong?
If you're looking for a modern answer using ES6:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from
var nodes = document.querySelectorAll('div');
nodes = Array.from(nodes);
Old question, but here is a tried & true method:
var nodes=document.querySelectorAll("div"); // returns a node list
nodes=Array.prototype.slice.call(nodes); // copies to an array
Explanation
document.querySelectorAll uses a CSS-style selector to find elements and returns a node list. It works as of IE 8.
The slice method copies a portion of an array-like collection (in this case all of it) into a new array.
call allows you to borrow a method from one object to use on another
To find the node list you could also have used `document.getElementsByTagName(), but this one is more flexible.
First, don't use document.all -- it's non-standard and deprecated. Use document.getElementsByTagName to get the DIV elements in your case.
Second, don't extend DOM objects such as NodeList -- built-in objects are a very strange breed and are not required to behave like any other objects that you generally work with. See this article for an in-depth explanation of this: What's wrong with extending the DOM.
IE doesn't support NodeList in the standard way. This is why you should roll your own namespace and NOT extend browser core objects.
You can do alert( typeof window.NodeList ) and see if it's undefined or not.

Remove element created by JavaScript on load

How can I remove elements which are created by JavaScript, I tried using CSS by setting display:none; but that doesn't seem to solve my problem, also I can't remove them since I don't have them in HTML, any other ways? Thank you
UPDATE:
Can't use any JavaScript such as jQuery, MooTools, ExtJS etc, and actual elements I want to remove are divs, with a specified class so I can't use getElementById.
I found this script on Google, but it doesn't seem to work but this is what I need:
HERE
This is fairly simple to do this using jQuery.
$("#myId").remove();
will remove
<div id="myId"></div>
Edit: You can also do it with "old school" javascript.
The function you're looking for is removeChild()
Example:
function removeElement(divNum) {
var d = document.getElementById('myDiv');
var olddiv = document.getElementById(divNum);
d.removeChild(olddiv);
}
You will want something like this to take advantage of browser support where you can:
if(document.getElementsByClassName){
// Safari 3+ and Firefox 3+
var itms = document.getElementsByClassName('your_class');
for(var i = 0; i<itms.length; i++){
var it = itms[i];
if(it.tagName == "DIV"){
it.parentNode.removeChild(it);
i = i - 1; //It actually gets removed from the array, so we need to drop our count
}
}
} else {
// All other browsers
var itms = document.getElementsByTagName('div');
for(var i = 0; i<itms.length; i++){
var it = itms[i];
// Test that className contains your class
if(/your_class/.test(it.className)) it.parentNode.removeChild(it);
}
}
JavaScript handles all memory mangement for you using garbage collection so once all references to an object cease to exist that object will be handled by the browsers specific implementation.
If you have the dom element itself:
if(node && node.parentNode){
// return a ref to the removed child
node.parentNode.removeChild(node);
}
Since you say you can't use Javascript, you're pretty stuck. Have you tried this CSS:
.classname {
display: none !important;
}
It's possible that the created elements have inline styles set on them, in which case normal CSS is overridden. I believe the !important will override inline styles.
Of course, the best solution is not to add the element in the first place... but I'm guessing you're in one of those (unfathomably common) scenarios where you can't change or get rid of the JS?
Not all browsers have a
document.getElementsByClassName
method, but for your purpose you can
fake it well enough- This method does
not work like the native
HTMLElement.getElementsByClassName- it
returns an array, not a live nodelist.
You can specify a parent element and a
tag name to speed it up.
function getElementsByClass(css, pa, tag){
pa= pa || document.body;
tag= tag || '*';
css= RegExp('\\b'+css+'\\b');
var A= [], elements, L, i= 0, tem;
elements= pa.getElementsByTagName(tag);
L= elements.length;
while(i<L){
tem= elements[i++];
if(css.test(tem.className)) A[A.length]= tem;
}
return A;
}
// test
var A= getElementsByClass('removeClass'), who;
while(A.length){
who= A.pop(); // remove from the end first, in case nested items also have the class
if(who.parentNode) who.parentNode.removeChild(who);
who= null;
}
If you have assigned event handlers to
the elements being removed, you should
remove the handlers before the
elements.
You will probably have to be more specific.
The general answer is 'with JavaScript'. As long as you have a way of navigating to the element through the DOM, you can then remove it from the DOM.
It's much easier if you can use a library like jQuery or prototype, but anything you can do with these you can do with JavaScript.
marcgg has assumed that you know the ID of the element: if you don't but can trace it in the DOM structure, you can do something like this (in prototype - don't know jQuery)
var css_selector = 'table#fred tr td span.mydata';
$$(css).invoke('remove');
If you can't use a JS library, you'll have to do the navigation through the DOM yourself, using Element.getElementsByTagName() a lot.
Now you've specified your question a bit: use Element.getElementsByTagName, and loop through them looking at their className property.
Use:
document.removeChild('id_of_element');

Categories

Resources