Reputation: 24019
I have the following menu and I want to add a class 'active' to the last clicked link. Using native javascript not with JQuery.
Firstly, I'm removing active from all links then trying to add 'active' to clicked link
Here's what I'm trying but simply does nothing:
<a href="#" class="menu active" onclick="changeClass()">test 1</a>
<a href="#" class="menu" onclick="changeClass()">test 2</a>
<a href="#" class="menu" onclick="changeClass()">test 3</a>
function changeClass() {
document.getElementsByClassName('menu').classList.remove('active');
this.className('menu active');
}
Here's a fiddle to have a look at but not if jsfiddle is working correctly
Upvotes: 1
Views: 124
Reputation: 41715
You've got a few problems:
1. document.getElementsByClassName()
returns an HTMLCollection
which has no classList
property. You have to access the items within the collection in order to access their properties, i.e.:
document.getElementsByClassName('menu')[0].classList = "foo";
2. Element.className
is a property, not a function. You must assign a value like so:
this.className = 'menu active';
3. When you attach a handler using inline markup, i.e.: <a onclick="foo()">
, this
(in the context of foo
) is not the element that raised the event; it's the global object, window
. You might consider attaching those handlers programmatically instead (fiddle):
function menuClickHandler(e) {
// on click...
// if the event target is already active, return
if (this.classList.contains("active")) return;
// if the event target is not already active...
// get an element that has both menu and active classes
// apparently only one item is active at any time
var active = document.querySelector(".menu.active");
// did we find an item? remove the active class
active && active.classList.remove("active");
// add the active class to the event target, "this"
// which is actually the appropriate element now
this.classList.add("active");
}
// iterate over all menu items
[...document.querySelectorAll(".menu")].forEach(item =>
// add a click handler to each item
item.addEventListener("click", menuClickHandler)
);
See also: document.querySelector()
, document.querySelectorAll()
, Array.prototype.forEach()
, and EventTarget.addEventListener()
Upvotes: 1
Reputation: 2429
There are a couple of things wrong with your code.
document.getElementsByClassName
does not return a single element, but an HTMLCollection
, which is basically an array with additional properties. This means that you have to iterate over it in order to remove "active" from the classlists of all included elements.
var collection = document.getElementsByClassName("menu"),
i;
for (i = 0; i < collection.length; i++) {
collection[i].classList.remove("active");
}
this
does not point to the element you clicked on, but to the window object. In order to use the event target in your function, you have to pass it as a parameter inside the HTML code and use this parameter inside your function.
<a href="#" class="menu active" onclick="changeClass(this)">test 1</a>
...
function changeClass(target) {
...
target.classList.add("active");
}
As you can see in the code above, you can also use Element.classList.add
to add a class to your element. If you use classList
in one case, you should probably use it in all cases. Older browsers do not support this feature, though. If you want to write backwards compatible code, you can use the className
attribute.
collection[i].className = "menu"; // Removing the action class
target.className = "menu active"; // Adding the action class
Upvotes: 1
Reputation: 1075735
className
is not a function, so this line:
this.className('menu active');
causes an error you can readily see in the web console.
As you're using classList
elsewhere, you could do:
this.classList.add('active');
Alternately, completely overwrite the classes:
this.className = 'menu active';
(Note that older browsers don't have classList
, so for them you'll need a shim if you rely on it.)
Separately, this
within your function will not be the element that was clicked, because of the way you have the event handler hooked up. You can ensure that it is this
with a minimal change using call
:
<a href="#" class="menu active" onclick="changeClass.call(this)">test 1</a>
<a href="#" class="menu" onclick="changeClass.call(this)">test 2</a>
<a href="#" class="menu" onclick="changeClass.call(this)">test 3</a>
You might even want to pass the event in:
<a href="#" class="menu active" onclick="changeClass.call(this, event)">test 1</a>
<a href="#" class="menu" onclick="changeClass.call(this, event)">test 2</a>
<a href="#" class="menu" onclick="changeClass.call(this, event)">test 3</a>
Or of course use modern techniques to hook up the handlers instead of attributes.
Upvotes: 3
Reputation: 7666
You need to pass the element on the onclick event and then change the class using the .className
HTML Code
<a href="#" class="menu active" onclick="changeClass(this)">test 1</a>
<a href="#" class="menu" onclick="changeClass(this)">test 2</a>
<a href="#" class="menu" onclick="changeClass(this)">test 3</a>
JS Code
function changeClass(elm) {
var elements = document.getElementsByClassName('active');
elements[0].className = "menu";
elm.className = 'menu active';
}
Upvotes: 0
Reputation: 2305
className is a property, not a method, so you should use it like this:
this.className = 'menu active'
And be aware that classList works in IE10+, and not below.
Upvotes: 1