Remove class on adding class to another element - javascript

I have a long list of image elements, each with it's own ID. I've already set it up so that when you click on one image, it will toggle a class "foo".
What I'd like to do is set it up so that when you click on another image, the first image's class "foo" will be removed. I'd like to do this with pure javascript if possible. Thanks.
Here's a fiddle: http://jsfiddle.net/q3aRC/
function clicked($id) {
document.getElementById($id).classList.toggle('foo');
}

I'd suggest, given that you're already using the classList api:
function clicked($id) {
// get all the elements with the class-name of 'foo':
var others = document.querySelectorAll('.foo');
// if there *are* any elements with that class-name:
if (others.length){
// iterate through that collection, removing the class-name:
for (var i = 0, len = others.length; i < len; i++){
others[i].classList.remove('foo');
}
}
/* add the class-name back (if it already had the class-name
we removed it in the previous section): */
document.getElementById($id).classList.add('foo');
}
JS Fiddle demo.
References:
document.querySelectorAll().
element.classList.

I would add a common class to all images and remove the foo class from all of them. Then I would add the class to the specific image
function clicked(id){
var images = document.getElementsByClassName('images');
for (var i = 0; i < images.length; ++i) {
images[i].classList.remove('foo');
}
document.getElementById(id).classList.add('foo');
}

Since you are already using classList I assume you're only catering to browsers new enough for addEventListener(), so I'd suggest removing all of the onclick attributes and doing something like this:
document.addEventListener('click',function(e){
if (e.target.tagName === "IMG") {
var imgs = document.getElementsByTagName('IMG');
for (var i = 0; i < imgs.length; i++)
if (imgs[i] != e.target)
imgs[i].classList.remove('foo');
e.target.classList.toggle('foo');
}
}, false);
Demo: http://jsfiddle.net/q3aRC/3/
That is, bind a single click handler to the document (or you could bind to a parent element of the images if they share a common parent), and then on click test if the clicked item is one of the elements you care about (i.e., an img) and go from there... The JS ends up about the same length, but the html ends up shorter and neater. You could actually remove the id attribute too if you weren't using it for anything other than your original clicked() function.
I used getElementsByTagName() just to show you yet another way of doing it, but getElementsByClassName() or querySelectorAll() (as in the other answers) are probably better options. But that's an easy switch to make.

Related

documents get elements by classname apply to all element

I am new to JS. I want to change css for all elements selected by className. I did some search and I found the solution below. But, it will only affect the first element. I am wondering if there is a easy to to change the css for all selected elements.
document.getElementsByClassName('ads')[0].style.display = 'none';
document.getElementsByClassName('class')[x]
may be x can be 0,1,2,... depends on elements with that class if there two div with ads
class you will count from 0 as the first div with that class because it returns in array
or you can use tag name while you are selecting
document.getElementsByTagName('div')[x]
this also returns in array because in html suppose to be many similar tags means that you have to index to them
document.getElementById('id')
this is selecting by using the tag's id and an id there a tag with an id should be the unique thats why this doesn't return in array means that you don't need to index on it
document.querySelectorAll('p .class')[x]
with selector you do it like you do in css but it returns in array to and also you can apply the pseudo classes and elements
document.querySelector('p .class')
this used like the above one but it didn't return in array so you don't have to index on it
NodeList.prototype.forEach = NodeList.prototype.forEach || Array.prototype.forEach;
document.querySelectorAll('.ads').forEach(ele => {ele.style.display = 'none'});
This should do it. The querySelectorAll-method returns you a Nodelist from a given css-selector. The first line checks if there is already a forEach method and if not it will inherit it from the Array Object.
You can also read about the from method of the Array object this could be a more cleaner version.
You need to use loop, please check snippet
NOTE: I added '.hide' class and set background color just to display result. you just add display:none in CSS
window.onload = function() {
var ads = document.getElementsByClassName("ads"),
len = ads !== null ? ads.length : 0,
i = 0;
for(i; i < len; i++) {
ads[i].className += " hide";
}
}
.hide {
background: skyblue;
}
<div class="ads">1</div>
<div class="ads">2</div>
Simple with jQuery method :
$('.ads:first').hide();
$('.ads:first').css("display":"none");
To using jQuery, you have to add jQuery plugin inside <body> tag before jQuery function call :
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

Hide nav when clicking on anchor tags (Vanilla JavaScript)

I have a nav which is displayed on small screens. My goal is to hide that nav each time the anchor tags in it are clicked.
I've tried multiple options until now, but none of them seem to work. I think the real problem here is that I don't know how to select all the anchor tags in the menu. The solution that I'm looking has to be pure JS. Thank you in advance.
<nav id="mySidenav" class="sidenav">
About
Services
Contact
</nav>
.sidenav {
width: 0;
}
.sidenav.mobile-only {
width:250px;
}
function closeNav() {
myNav.classList.remove("mobile-only");
}
Option 1
var anchorTags = document.querySelectorAll('a')
anchorTags.addEventListener('click', closeNav(), false);
** Uncaught TypeError: anchorTags.addEventListener is not a function
Option 2
var anchorTags = document.getElementsByTagName("a")
anchorTags.addEventListener('click', closeNav());
**Uncaught TypeError: anchorTags.addEventListener is not a function
Option 3
var anchorTags = document.querySelector("mobile-only a");
for (var x = 0; x < anchorTags.length; x++) {
anchorTags[x].addEventListener("click", function() {
closeNav();
});
};
**Uncaught TypeError: Cannot read property 'length' of null
The only solution that I've found was to add the function inline, but I'm sure there has to be a better solution.
About
Services
Portofolio
Later edit:
Option 4: The winner (there are better solutions than this which are listed bellow. Thanks everyone)
var anchorTags = document.getElementById("mySidenav").childNodes;
for (var x = 0; x < anchorTags.length; x++) {
anchorTags[x].addEventListener("click", function() {
closeNav();
});
};
Thank you everyone for your responses, I finally understood what the problem was. I've tried all your solutions and they work perfectly.
We're going to go with Option 3 since it is closet to the solution.
You should be using document.querySelectorAll('nav a') or even better document.querySelectorAll('.sidenav a'). This will get you a node list of all elements that matched our selector. After that we can use forEach to loop over each node and bind our click event.
document.querySelectorAll('.sidenav a').forEach(function(a) {
a.addEventListener('click', closeNav);
});
Note that I used closeNav instead of closeNav() - the () are important! Using () will try to invoke the function immediately rather than assigning the function as the handler for the event.
In option 1, what returns after running querySelectorAll, is a nodeList which is similar to an array. You can't add an event listener to the list itself, you'll need to add the listener to each item in the list similar to what you've attempted to do in option 3. In option 2, it is the same problem, but this time with an HTMLCollection. In option 3, you've done querySelector rather than querySelectorAll which only returns the first element that matches the selector. so if you try option 3 with querySelectorAll, it should work.
When you use getElementsByTagName what it returns is a Node collection, not a HTMLElement, therefore you need to loop it to add to each element the event listener
var anchorTags = document.getElementsByTagName("a");
[...anchorTags].map(elem => elem.addEventListener('click', closeNav));
Same thing with querySelector, it returns a HTMLElement (only one) and if you want them all use querySelectorAll, you need to pass it a CSS Selector
var anchorTags = document.querySelector("a.mobile-only");
[...anchorTags].map(elem => elem.addEventListener('click', closeNav));
I used array spread [...arraish_element] to give to the Node Collection all the methods of a true array and being able to use map there, or you can just use a for with the length of the nodecollection

Adding a class to all elements that match a given dataset in JavaScript

I'm trying to move away from jQuery and to plain JavaScript, and I'm having trouble understanding how to do something. I want to have a very flexible script that will allow for multiple buttons to toggle the same menu. At first, I had this working with:
// function to add active classes on a menu and it's associated button
function toggle_menu() {
var active_class = "is-active";
var target_menu = this.dataset.menu;
var button_class_list = this.classList;
var menu_class_list = document.querySelector("[data-menu=" + target_menu + "]").classList;
button_class_list.toggle(active_class);
menu_class_list.toggle(active_class);
}
// fire toggle_menu when any menu-button is clicked
document.querySelector(".menu-button").addEventListener("click", toggle_menu);
But after thinking about it, I realized that this could allow for the menu and the buttons to get out of sync. So now my plan is to sync all instances of the buttons and the menus, such as that when you click one button, all buttons with the same [data-menu] get toggled to the same state.
But I'm confused as to how to go about this. I thought if I where to do something like
document.querySelector("[data-menu=" + target_menu + "]").toggleClass("is-active");
it would work fine, but turns out that's only affecting the first instances of a matching element, not all matching elements.
How can I change this so that all elements on the page with the same data-menu attribute always keep their is-active classes in sync?
Try something like:
var selected = document.querySelectorAll("[data-menu=" + target_menu + "]");
Array.from(selected).forEach(function(item){
item.classList.toggle("is-active");
}));
Please check querySelectorAll
Returns a list of the elements within the document (using depth-first
pre-order traversal of the document's nodes) that match the specified
group of selectors. The object returned is a NodeList.
You wanna use querySelectorAll:
var elems = document.querySelectorAll("[data-menu=" + target_menu + "]");
for (var i = 0; i < elems.length; ++i) {
elems[i].toggleClass("is-active");
}
This is the same as querySelector, but it returns all instances instead of just the first

JS select child of this with specific class

I am writing a GreaseMonkey script that goes through a page with various elements and each element has text and a button. It uses document.getElementsByClassName to find the parent elements, and it has a for loop to do something to each parent element. In this loop, I need to select a child node with a specific class and find its text value (innerHTML). I can't figure out how to select the child with a specific class of this element.
You'll want to grab the currently iterated element and use querySelector()
For example:
var elements = document.getElementsByClassName('class');
for (var i = 0, len = elements.length; i < len; i++) {
var child = elements[i].querySelector('.class_of_desired_element');
//do stuff with child
}
Note the dot before the class name in querySelector as it works similar to jQuery.
Try querySelectorAll(), which you can use to find elements within the current element.
var parent = document.getElementsByClassName('parentClass'),
parent[0].querySelectorAll('.childClass');
Depending on exactly what you are looking to do, you could also skip selecting the parent, if you don't explicitly need a reference to it.
document.querySelectorAll('.parentClass .childClass');
https://developer.mozilla.org/en-US/docs/Web/API/Element.querySelectorAll
You can use
var yourelement = document.getElementsByClass("");
var i = yourelement.nextSibling;
var e = i.nextSibling;
and keep getting the nextSibling of the element till you get it.
However, like #teddy said in the comments, I would suggest you use jQuery. It has a MUCH easier way to do it:
var value = $('.parentClass .childClass').html();

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