How do I use getclassbyelemenentnames? - javascript

So currently I have this bit of code:
function bgchanger(color){
document.getElementById("headwrapper").style.backgroundColor = color;
document.getElementById("footerwrapper").style.backgroundColor = color;
}
Accompanied by this HTML:
<div id="blue" style="cursor: pointer" onClick="bgchanger('blue')"></div>
It works well for changing the color of divs on click. However, it's notably inefficient. Rather than identifying two divs by Element Id, I should be calling them by a shared class. But I'm a little unclear on exactly how I would arrange a document.getElementsByClassName since it's apparently arranged differently than the ID method.

I assume you mean inefficient to code, since at runtime getElementById() will be fast. But as you imply, your JS would be neater if it could do this sort of thing by class rather than having a bunch of hard-coded ids. Indeed if your JS no longer needs to use ids you can omit them from your html too (unless they're used in your stylesheet).
So, getElementsByClassName() returns a list, so just loop through it:
function bgchanger(color){
var list = document.getElementsByClassName("someClass");
for (var i = 0; i < list.length; i++) {
list[i].style.backgroundColor = color;
}
}
Demo: http://jsfiddle.net/KNxBV/
Update with cookie monster's suggestion:
You can use querySelectorAll() instead of getElementsByClassName() - it has slightly better browser support:
var list = document.querySelectorAll(".someClass");
// other code as above. Note the . ---^
Updated demo: Demo: http://jsfiddle.net/KNxBV/1/

Related

Why does jQuery return more than one element when selecting by type and ID? [duplicate]

I fetch data from Google's AdWords website which has multiple elements with the same id.
Could you please explain why the following 3 queries doesn't result with the same answer (2)?
Live Demo
HTML:
<div>
<span id="a">1</span>
<span id="a">2</span>
<span>3</span>
</div>
JS:
$(function() {
var w = $("div");
console.log($("#a").length); // 1 - Why?
console.log($("body #a").length); // 2
console.log($("#a", w).length); // 2
});
Having 2 elements with the same ID is not valid html according to the W3C specification.
When your CSS selector only has an ID selector (and is not used on a specific context), jQuery uses the native document.getElementById method, which returns only the first element with that ID.
However, in the other two instances, jQuery relies on the Sizzle selector engine (or querySelectorAll, if available), which apparently selects both elements. Results may vary on a per browser basis.
However, you should never have two elements on the same page with the same ID. If you need it for your CSS, use a class instead.
If you absolutely must select by duplicate ID, use an attribute selector:
$('[id="a"]');
Take a look at the fiddle: http://jsfiddle.net/P2j3f/2/
Note: if possible, you should qualify that selector with a type selector, like this:
$('span[id="a"]');
The reason for this is because a type selector is much more efficient than an attribute selector. If you qualify your attribute selector with a type selector, jQuery will first use the type selector to find the elements of that type, and then only run the attribute selector on those elements. This is simply much more efficient.
There should only be one element with a given id. If you're stuck with that situation, see the 2nd half of my answer for options.
How a browser behaves when you have multiple elements with the same id (illegal HTML) is not defined by specification. You could test all the browsers and find out how they behave, but it's unwise to use this configuration or rely on any particular behavior.
Use classes if you want multiple objects to have the same identifier.
<div>
<span class="a">1</span>
<span class="a">2</span>
<span>3</span>
</div>
$(function() {
var w = $("div");
console.log($(".a").length); // 2
console.log($("body .a").length); // 2
console.log($(".a", w).length); // 2
});
If you want to reliably look at elements with IDs that are the same because you can't fix the document, then you will have to do your own iteration as you cannot rely on any of the built in DOM functions.
You could do so like this:
function findMultiID(id) {
var results = [];
var children = $("div").get(0).children;
for (var i = 0; i < children.length; i++) {
if (children[i].id == id) {
results.push(children[i]);
}
}
return(results);
}
Or, using jQuery:
$("div *").filter(function() {return(this.id == "a");});
jQuery working example: http://jsfiddle.net/jfriend00/XY2tX/.
As to Why you get different results, that would have to do with the internal implementation of whatever piece of code was carrying out the actual selector operation. In jQuery, you could study the code to find out what any given version was doing, but since this is illegal HTML, there is no guarantee that it will stay the same over time. From what I've seen in jQuery, it first checks to see if the selector is a simple id like #a and if so, just used document.getElementById("a"). If the selector is more complex than that and querySelectorAll() exists, jQuery will often pass the selector off to the built in browser function which will have an implementation specific to that browser. If querySelectorAll() does not exist, then it will use the Sizzle selector engine to manually find the selector which will have it's own implementation. So, you can have at least three different implementations all in the same browser family depending upon the exact selector and how new the browser is. Then, individual browsers will all have their own querySelectorAll() implementations. If you want to reliably deal with this situation, you will probably have to use your own iteration code as I've illustrated above.
jQuery's id selector only returns one result. The descendant and multiple selectors in the second and third statements are designed to select multiple elements. It's similar to:
Statement 1
var length = document.getElementById('a').length;
...Yields one result.
Statement 2
var length = 0;
for (i=0; i<document.body.childNodes.length; i++) {
if (document.body.childNodes.item(i).id == 'a') {
length++;
}
}
...Yields two results.
Statement 3
var length = document.getElementById('a').length + document.getElementsByTagName('div').length;
...Also yields two results.
What we do to get the elements we need when we have a stupid page that has more than one element with same ID? If we use '#duplicatedId' we get the first element only. To achieve selecting the other elements you can do something like this:
$("[id=duplicatedId]")
You will get a collection with all elements with id=duplicatedId.
From the id Selector jQuery page:
Each id value must be used only once within a document. If more than one element has been assigned the same ID, queries that use that ID will only select the first matched element in the DOM. This behavior should not be relied on, however; a document with more than one element using the same ID is invalid.
Naughty Google. But they don't even close their <html> and <body> tags I hear. The question is though, why Misha's 2nd and 3rd queries return 2 and not 1 as well.
If you have multiple elements with same id or same name, just assign same class to those multiple elements and access them by index & perform your required operation.
<div>
<span id="a" class="demo">1</span>
<span id="a" class="demo">2</span>
<span>3</span>
</div>
JQ:
$($(".demo")[0]).val("First span");
$($(".demo")[1]).val("Second span");
Access individual item
<div id='a' data-options='{"url","www.google.com"}'>Google</div>
<div id='a' data-options='{"url","www.facebook.com"}'>Facebook</div>
<div id='a' data-options='{"url","www.twitter.com"}'>Twitter</div>
$( "div[id='a']" ).on('click', function() {
$(location).attr('href', $(this).data('options').url);
});
you can simply write $('span#a').length to get the length.
Here is the Solution for your code:
console.log($('span#a').length);
try JSfiddle:
https://jsfiddle.net/vickyfor2007/wcc0ab5g/2/

Change CSS attributes on part of a HTML element's innerHTML with Javascript

I have been trying to find a way to change part of the innerHTML of several elements with the class .studentYear, but I keep getting a JavaScript undefined error. Here's what I am trying.
JavaScript:
document.getElementsByClassName('studentYear').innerHTML.indexOf(" ").css('color', '#800000');
HTML:
<div class="accordionContainer">
<div class="studentYear">Freshman (A1 Schedule)</div>
<div class="accordionContent" style="display:none;">
<p class="studentTerm">Fall</p>
<p>Content</p>
</div>
</div>
I see now that it is a collection (array type). I need to access the innerHTML for each item in the collection and change the style attribute on only part of the innerHTML, in this case everything in parentheses "(A1 Schedule)". I still get the undefined error if following the suggestions made below.
There are a number of problems with what you're trying.
document.getElementsByClassName returns an HTMLCollection, which is kind of like an array, but not quite, so you can't call innerHTML on it directly
indexOf(" ") will give you the first index of the space, which would possibly be before the element. It would also give you a number.
css() doesn't exist in vanilla JavaScript, that's a jQuery thing.
It looks like you're wanting to set that element to use color: #000000. That means you actually need to set the style attribute.
This code would do the trick, applying it to every one of them with the class studentYear.
Array.prototype.forEach.call(document.getElementsByClassName('studentYear'), function (element) {
element.style = 'color: #F00';
});
The Array.prototype.forEach.call lets us treat the HTMLCollection as an array and loop through it. Then for each element, we'll set it's style attribute to color: #F00 (or whatever you want, I used red to make it obvious).
If you need to support older browsers, you shouldn't access the style attribute directly, instead use setAttribute instead:
element.setAttribute('style', 'color: #F00');
As the comments have said, you have to loop through the objects and set each one since it is similar to an array. Something like this:
var items = document.getElementsByClassName('studentYear');
for (var i = 0; i < items.length; i++) {
items[i].setAttribute('style', 'color: #F00');
}
Edit: Just saw samanime's answer, which seems a little more elegant in my opinion.

Using arrays to store multiple IDs or classes, and then manipulating them in functions

I have a set of classes stored in a JavaScript array called 'targets' as follows:
HTML
<div id="one">1</div>
<div id="two">2</div>
<div id="three">3</div>
<div id="four">4</div>
<div id="five">5</div>
<div id="six">6</div>
<div id="seven">7</div>
<div id="eight">8</div>
<div id="nine">9</div>
<div id="ten">10</div>
JavaScript — Array
var targets = ["#one", "#two", "#three", "#four", "#five", "#six", "#seven", "#eight", "#nine", "#ten"];
I would then like to manipulate them to use in functions. Depending on my situation, I'd either want to target one specific ID from the array, or all entries in the array at once. I do understand that each entry into an array is given a unique index number, starting with 0. Therefore, #one is index 0, and #ten is index 9.
I also understand that I can use for loops with arrays, that will cycle through each entry. My issue is that I can't seem to get my function working when it is called from a click event (on an input type="button" element).
Here are two examples of my code. One targets one specific entry, the other targets them all. Or at least, that is what it should be doing. Chrome's console shows no errors with my code, and I have never really worked with arrays, so this is a first for me.
JavaScript — Attempting to Target a Single Entry (Working)
$(targets[0]).addClass('hide').removeClass('show');
JavaScript - Attempting to Target All Entries I
$(targets).each(function() {
$(this).addClass('hide').removeClass('show');
});
JavaScript - Attempting to Target All Entries II
targets.forEach(function() {
$(targets).addClass('hide').removeClass('show');
});
JavaScript - Attempting to Target All Entries III
var i;
for ( i = 0; i < targets.length; ++i ) {
$(targets).addClass('hide').removeClass('show');
}
Also, I know that jQuery allows you to manipulate multiple classes and/or IDs at once by passing multiple values through the $() part of the code, but I'm in a predicament where the entries of the array will be different, depending on user input, and I feel that I will be manipulating in excess of 250 different IDs and classes in total. I've simply omitted the HTML code to the first 10 elements.
The majority of the 250 IDs/classes, are located on <option> elements. The .show and .hide classes used in the code are not applied directly to the <option> elements themselves, but to <select> elements that contain these options. Each <select> element has a unique ID.
If I can just figure out how to do this with arrays, everything will be fine.
Thanks.
You have to join the array in to a string that is a valid jQuery selector
$(targets.join(', ')).addClass('hide').removeClass('show');
This will give you
$("#one, #two, #three ...")
which is valid, an array of ID's is not a valid selector
Also note that showing and hiding option elements is not cross browser
Commenting on your approaches in order:
JavaScript — Attempting to Target a Single Entry (Working)
This one is working (as you say). You can use similar techniques to get at ALL elements in the array of id-s:
$(targets[0]).addClass('hide').removeClass('show');
$(targets.join(",")).addClass('hide').removeClass('show');
However the common way is to add a class attribute rather than an id. After all - remember - that's what classes are for. Any DOM element can (and usually does) have a multitude of classes.
<div id="one" class="number">1</div>
<div id="two" class="number">2</div>
<!-- ... and so on -->
The above two lines become equivalent to:
// These two lines are each equivalent to $(target[0])
$(".number").eq(0).addClass('hide').removeClass('show'); // or even...
$(".number")[0].addClass('hide').removeClass('show');
// This is equivalent to $(target.join(","))
$(".number").addClass('hide').removeClass('show');
JavaScript - Attempting to Target All Entries I
The .each function can only be called on jQuery objects. You can't use it on an array, but you can use $.each instead:
$.each(targets, function(idx, obj) {
$(obj).addClass('hide').removeClass('show');
});
JavaScript - Attempting to Target All Entries II
When using forEach, you need to (ok, not need to, but at least should) make use of the parameters passed to your callback:
targets.forEach(function(elt, idx, arr) {
$(elt).addClass('hide').removeClass('show');
// ... OR ...
$(arr[idx]).addClass('hide').removeClass('show');
// ... OR ...
$(targets[idx]).addClass('hide').removeClass('show');
});
JavaScript - Attempting to Target All Entries III
$(targets) selects nothing. It doesn't throw an error (though it probably ought to). You access array elements with the [index] notation:
for (var i = 0; i < targets.length; ++i ) {
$(targets[i]).addClass('hide').removeClass('show');
}
In your for-loop you're query-ing the whole list everytime and not specific elements, (add [i] to targets)
var i;
for ( i = 0; i < targets.length; ++i ) {
$(targets[i]).addClass('hide').removeClass('show');
}

Remove all classes except one

Well, I know that with some jQuery actions, we can add a lot of classes to a particular div:
<div class="cleanstate"></div>
Let's say that with some clicks and other things, the div gets a lot of classes
<div class="cleanstate bgred paddingleft allcaptions ..."></div>
So, how I can remove all the classes except one? The only idea I have come up is with this:
$('#container div.cleanstate').removeClass().addClass('cleanstate');
While removeClass() kills all the classes, the div get screwed up, but adding just after that addClass('cleanstate') it goes back to normal. The other solution is to put an ID attribute with the base CSS properties so they don't get deleted, what also improves performance, but i just want to know another solution to get rid of all except ".cleanstate"
I'm asking this because, in the real script, the div suffers various changes of classes.
Instead of doing it in 2 steps, you could just reset the entire value at once with attr by overwriting all of the class values with the class you want:
jQuery('#container div.cleanstate').attr('class', 'cleanstate');
Sample: http://jsfiddle.net/jtmKK/1/
Use attr to directly set the class attribute to the specific value you want:
$('#container div.cleanstate').attr('class','cleanstate');
With plain old JavaScript, not JQuery:
document.getElementById("container").className = "cleanstate";
Sometimes you need to keep some of the classes due to CSS animation, because as soon as you remove all classes, animation may not work. Instead, you can keep some classes and remove the rest like this:
$('#container div.cleanstate').removeClass('removethis removethat').addClass('cleanstate');
regarding to robs answer and for and for the sake of completeness you can also use querySelector with vanilla
document.querySelector('#container div.cleanstate').className = "cleanstate";
What if if you want to keep one or more than one classes and want classes except these. These solution would not work where you don't want to remove all classes add that perticular class again.
Using attr and removeClass() resets all classes in first instance and then attach that perticular class again. If you using some animation on classes which are being reset again, it will fail.
If you want to simply remove all classes except some class then this is for you.
My solution is for: removeAllExceptThese
Array.prototype.diff = function(a) {
return this.filter(function(i) {return a.indexOf(i) < 0;});
};
$.fn.removeClassesExceptThese = function(classList) {
/* pass mutliple class name in array like ["first", "second"] */
var $elem = $(this);
if($elem.length > 0) {
var existingClassList = $elem.attr("class").split(' ');
var classListToRemove = existingClassList.diff(classList);
$elem
.removeClass(classListToRemove.join(" "))
.addClass(classList.join(" "));
}
return $elem;
};
This will not reset all classes, it will remove only necessary.
I needed it in my project where I needed to remove only not matching classes.
You can use it $(".third").removeClassesExceptThese(["first", "second"]);

using document.getElementsByTagName doesn't find elements added dynamically (IE6)

I am trying to write a method that grabs all the elements of a certain classname for browsers that don't have the 'getElementsByClassName' method. This works perfectly for elements that are generated server-side, however the page has the ability to add elements dynamically for some reason 'window.document.all' does not get these dynamic elements. Any ideas? Method below.
function getClassName(class) {
var i, neededStuff = [], elements = document.getElementsByTagName('*');
for (i = 0; i < elements.length; i++) {
if (elements[i].className == class) {
neededStuff[neededStuff.length] = elements[i];
}
}
return neededStuff;
}
class is a reserved keyword in IE. Don't use it literally. Change class to something like theClass.
Also, try document.getElementsByTagName('*') instead of document.all if changing class doesn't do it.
EDIT:
http://work.arounds.org/sandbox/72
Works perfectly for me in IE6 ^
Let me try dynamically adding...
EDIT #2: works fine..
http://work.arounds.org/sandbox/72
Use jQuery :)
http://jquery.com/
$('.ClassName')
will return your elements :)
then you can change it's value, add classes very easily!
Some great tutorials here
http://docs.jquery.com/Tutorials

Categories