What is the alternative way of doing something like
$(".myElement").each(function(){
//function
});
in plain Javascript?
This will iterate all divs in your current document. You can replace document.getElementsByClassName('someclass') etc. and do something with their attributes and values
var elements = document.getElementsByTagName('div');
for (var i = 0; i < elements.length; i++) {
doSomething(elements[i]);
}
Here is the jsfiddle: http://jsfiddle.net/allenski/p7w5btLa/
$(#myElement)
You are trying to iterate over a id selector. ID has to be unique in a HTML page.
If it's a class or element tags you want to iterate over you can use a for loop.
var $elems = $('.someClass');
for(var i=0; i< $elems.length; i++ ) {
// $elems[i] --> Gives you a `DOM` element
// $elems.eq(i) --> Gives you a `jQuery` object
}
Vanilla Javascript
var elems = document.getElementsByClassName('someClass');
for(var i=0;i< elems.length;i ++) {
elem[i] // Based on index
}
getElementsByTagName if you want to iterate over specific tags.
getElementsByName - get the elements based on name attribute.
You can also use document.querySelectorAll to get a list of objects and iterate over them.
var elems = document.querySelectorAll('.someClass')
for(var i=0; i<elems.length; i++) {
elems[i] // Give you DOM object
}
Alternative methods to the each function, here are two:
Setup
var $all = $('*');
var len = $all.length;
1
while(--len){
// use $($all[len]); for jQuery obj
var elem = $all[len];
doWork(elem);
}
2
//reset len for next loop
len = $all.length;
do {
var $elem = $all.filter(':eq('+ --len + ')');
doWork($elem);
} while (len);
var el = $(something);
for (var i = 0; i < el.length; i++) {
// do something with el[i] or more often with $(el[i])
}
el is a pseudo-array (or array-like Object) that has a length and elements accessible with the [] operator in the range 0...length-1. So you can do el[0], el[1] etc., but remember that el elements aren't jquery "objects", normally they are DOM elements, so you can't do el[0].text("Hello"). As in the each method, you have to do $(el[0]).text("Hello");
It's even wrong to do:
for (var i = 0; i < $(something).length; i++) {
// do something with el[i] or more often with $(el[i])
}
because in this way the $(something) will be recalculated every cycle.
You have to use a for loop. look at
http://www.w3schools.com/js/js_loop_for.asp
Related
I am trying to select elements on a class ,put them on an array. For each element in that class I want to select the "a" Tag and then I want to make a listener for each element, but this is a mess that seems impossible for me in JS. Heres the code I have so far.
var elemento = new Array();
var y=document.getElementsByClassName("thumbnails");
for (var i=0; i < y.length; i++) {
elemento = ( y[i].getElementsByTagName("a"));
elemento[0].addEventListener('click', function(){alert("jo")}, false);
}
This works for the first element but not for the rest, and yes, elemento is [0] because there's no more "a" tags inside each thumbnail.
One short way for modern browsers is to use CSS selectors in querySelector:
var elements = document.querySelectorAll('.thumbnails a');
for (var i = 0, len = elements.length; i < len; i++) {
elements[i].addEventListener('click', function() { ... }, false);
}
DEMO: http://jsfiddle.net/AApw2/
While .querySelectorAll is a good solution, it's important to understand how this would be handled without it.
What you simply need is an inner loop to loop over the current set of a elements held by the elemento variable.
var elemento;
var y = document.getElementsByClassName("thumbnails");
var handler = function(){alert("jo")};
for (var i=0; i < y.length; i++) {
elemento = y[i].getElementsByTagName("a");
for (var j = 0; j < elemento.length; j++) {
elemento[j].addEventListener('click', handler, false);
}
}
Notice that I use a different counter j for the inner loop since i is already in use.
Notice also that I moved the handler function to a variable outside the loop. This makes all the elements use the same function object, which is more efficient.
Side note:
You may also want to create some helper methods that will shorten the long method names, and convert them to Arrays. This allows you to use Array methods like .forEach().
var _slice = Function.call.bind([].slice);
function byClass(ctx, clss) {
if (typeof ctx === "string") {
clss = ctx;
ctx = document;
}
return _slice(ctx.getElementsByClassName(clss));
}
function byTag(ctx, tag) {
if (typeof ctx === "string") {
tag = ctx;
ctx = document;
}
return _slice(ctx.getElementsByTagName(tag));
}
That reduces your code to this:
var handler = function(){alert("jo")};
byClass("thumbnails").forEach(function(thumb) {
byTag(thumb, "a").forEach(function(a) {
a.addEventListener('click', handler, false);
});
});
I'd like to create a select element with a list of a user's Facebook friends (obtained as a JSON object). I hardcode <select id="friends"></select> into my HTML, then use the following Javascript code to parse the JSON and insert each friend as an option of the select element:
var msgContainer = document.createDocumentFragment();
for (var i = 0; i < response.data.length; i++) {
msgContainer.appendChild(document.createTextNode('<option value="'+response.data[i].id+'">'+response.data[i].name+'</option>'));
}
document.getElementById("friends").appendChild(msgContainer);
This almost works, except that it inserts < and > instead of < and >. How can I fix it, and is there a more efficient way to insert multiple HTML elements using pure Javascript (not JQuery)?
Not sure why you're creating a text node, but it would seem that you want to create option elements, so you could use the Option constructor instead.
var msgContainer = document.createDocumentFragment();
for (var i = 0; i < response.data.length; i++) {
msgContainer.appendChild(new Option(response.data[i].name, response.data[i].id));
}
document.getElementById("friends").appendChild(msgContainer);
Or you can use the generic document.createElement().
var msgContainer = document.createDocumentFragment();
for (var i = 0; i < response.data.length; i++) {
var option = msgContainer.appendChild(document.createElement("option"));
option.text = response.data[i].name;
option.value = response.data[i].id;
}
document.getElementById("friends").appendChild(msgContainer);
It's nice to have a helper function for creating elements and setting properties at the same time.
Here's a simple example of one:
function create(name, props) {
var el = document.createElement(name);
for (var p in props)
el[p] = props[p];
return el;
}
It can be expanded to cover some specific needs, but this will work for most cases.
You'd use it like this:
var msgContainer = document.createDocumentFragment();
for (var i = 0; i < response.data.length; i++) {
msgContainer.appendChild(create("option", {
text: response.data[i].name,
value: response.data[i].id
}));
}
document.getElementById("friends").appendChild(msgContainer);
Try this in your for loop instead:
var o = document.createElement('option');
o.setAttribute('value', response.data[i].id);
o.appendChild(document.createTextNode(response.data[i].name));
msgContainer.appendChild(o);
For those who need similar functionality, you can generate an html snippet using template literals and insert it using innerHTML property. Plus you can set attributes and selected while iterating over the items:
const el = document.createElement('select');
el.innerHTML = ['John', 'Sally', 'Betty'].reduce((acc, prev, i) => {
if (i === 1) {
return acc + `<option selected>${prev}</option>`;
}
return acc + `<option>${prev}</option>`;
}, '');
const root = document.querySelector('#app');
root.appendChild(el);
In modern browsers this is faster than creating elements one by one imperatively.
The end result I'm after is a JavaScript array containing a list of tag names that are used in the HTML document eg:
div, span, section, h1, h2, p, etc...
I want the list to be distinct and I'm not interested in tags within the <head> of the document (but they can be there if it's a performance hog to exclude them).
This has to work in IE 6, 7, & 8 and I don't want to use jquery.
What would be the most efficient way of doing this?
What you're looking for is document.all.tagName
At the top of my head, a for loop like this should do it (providing that you're gonna filter the tags you don't want on that list)
for(i = 0; i < document.all.length; i++)
{
console.log(document.all[i].tagName);
}
Here is a cross-browser solution:
var tags = {}; // maintains object of tags on the page and their count
var recurse = function(el) {
// if element node
if(el.nodeType == 1) {
if(!tags[el.tagName])
tags[el.tagName] = 0;
tags[el.tagName]++;
}
// recurse through the children
for(var i = 0, children = el.childNodes, len = children.length; i < len; i++) {
recurse(children[i]);
}
}
recurse(document);
// if you want just what's in the body(included)
var bodies = document.getElementsByTagName("body");
for(var i = 0; i < bodies.length; i++)
recurse(bodies[i]);
To get a unique list of tagnames in a document as an array that works in all browsers back to IE 6 and equivalent:
function listTagNames() {
var el, els = document.body.getElementsByTagName('*');
var tagCache = {};
var tagname, tagnames = [];
for (var i=0, iLen=els.length; i<iLen; i++) {
tagname = els[i].tagName.toLowerCase();
if ( !(tagname in tagCache) ) {
tagCache[tagname] = tagname;
tagnames.push(tagname);
}
}
return tagnames;
}
If you think there might be an inherited object property that is the same as a tag name, use a propertyIsEnumerable test:
if (!tagCache.propertyIsEnumerable(tagname)) {
so it will work even in Safari < 2.
Get all tagnames in the document, unique, crossbrowser, plain js:
var els = document.getElementsByTagName('*'), tags = [], tmp = {}
for (var i=0;i<els.length;i+=1){
if (!(els[i].tagName in tmp)){
tags.push(els[i].tagName);
tmp[els[i].tagName] = 1;
}
}
use
if (!(els[i].tagName in tmp)
&& !/head/i.test(els[i].parentNode.tagName)
&& !/html|head/i.test(els[i].tagName))
to exclude the <head>
I have several radio buttons on a form all grouped in 3's, and when they are clicked I need to run a JS function on them. In this function I loop through the radio buttons in the group of the button that was pressed (i.e. if the group was called 'abc_name' id use for (var i = 0; i < form.abc_name.length; i++){ }).
I'm wondering if there is a way to action a group of radio buttons in the same way using a constructed group name? For example if I passed 'xyz' to the function I'd need the code to be for (var i = 0; i < form.xyz_name.length; i++){ }. Thanks.
Use square bracket notation.
function loopDeLoop (xyz) {
var elems = form.elements[xyz + "_name"],
len = elems.length,
i;
for (i=0;i<len;i++){
console.log(elems[i];
}
}
You should really be using getElementsByName() in both cases;
for (var els = document.getElementsByName(xyz + '_name'), i=0; i<els.length;i++) {
// something on els[i]
}
Although the direct equivalent for what you've got would be:
for (var els = form.elements[xyz + "_name"];, i=0; i<els.length;i++) {
// something on els[i]
}
Use [] property syntax:
function loop(s) {
s += '_name';
var inputs = form[s];
for (var i = 0; i < inputs.length; ++i) {
...
}
}
This is necessary when the property key is a variable - a.b syntax only works if b is a "literal".
How do I get access to live DOM collections from jQuery?
Take for example this HTML <div id='a'></div> and this JavaScript code:
var a = $('#a');
var divs = a[0].getElementsByTagName('DIV');
for(var i=0; divs.length < 20; ) {
a.append($('<div>'+(i++)+'</div>'));
}
It will append 20 div children to <div id='a'> because divs is a live collection.
Is there anything in jQuery that I could replace the second line with to get the same result?
var divs = $('#a div'); results in infinite loop.
JSFiddle is here.
In case #a already contains divs:
var $a = $('#a'),
toAdd = Math.max(20 - $a.find('div').length, 0);
for (var i = 0; i < toAdd; i++) {
$a.append('<div>' + i + '</div>');
}
That would be equivalent to the code above.
Live Collections - the true ones, are not something which can be returned by modern jquery.
Moreover, modern method which is intended to replace in nearest future getElementsByTagName, getQuerySelectorAll also return a static collection.
This is the answer to question you've stated.
As for the question you've really wanted to ask, other users already tried to provide you some help.
Select the element each time, this will create a new jQuery object. Which I think it the only way if the element is changing.
var a = $('#a');
for(var i=0; $('#a div').length < 20; ) {
a.append($('<div>'+(i++)+'</div>'));
if(i==50) break;
}
EDIT:
Or this:
for(var i=0, a=$('#a'); a.children('div').length < 20; ) {
a.append($('<div>'+(i++)+'</div>'));
if(i==50) break;
}
Or this, just one selector:
var a = $('#a');
var length = a.children('div').length;
while(length < 20) {
a.append($('<div>'+(length++)+'</div>'));
}
How to get DOM live collections with jQuery?
That’s not possible.
This has the same effect as your example code, though:
var $a = $('#a');
for (var i = 0; i < 20; i++) {
$a.append('<div>' + i + '</div>');
}
http://jsfiddle.net/mathias/Af538/
Update: If the code needs to be repeated periodically, you could use something like this:
var $a = $('#a'),
toAdd = Math.max(20 - $('div', $a).length, 0),
i;
for (i = 0; i < toAdd; i++) {
$a.append('<div>' + i + '</div>');
}
http://jsfiddle.net/mathias/S5C6n/
Is it always 20 div children ?
Isn't it better to use the following
var a = $('#a div');
for(var i=0; i < 20; i++) {
a.append($('<div>'+(i)+'</div>'));
}
The syntax you're looking for is:
var divs = $('div#a');
Since IDs are supposed to be unique, you could just do:
var divs = $('#a');