Fade out siblings in a list (non jQuery) - javascript

I've created a script to fade out all items in a list when one element in the list is hovered.
I've sort of got it to work - but the last item in the list doesn't fade in (when the last item in the list is hovered all items are faded) - the rest of the list though works correctly.
My script is here -
var activeItems = document.querySelectorAll('.item');
for (i = 0; i < activeItems.length; i++) {
activeItems[i].addEventListener("mouseover", fadeOutItems, false);
activeItems[i].addEventListener("mouseout", resetListStyles, false);
function fadeOutItems() {
for (i = 0; i < activeItems.length; i++) {
this.setAttribute("class", "item fade-in");
activeItems[i].setAttribute("class", "item fade-out");
}
}
function resetListStyles() {
for (i = 0; i < activeItems.length; i++) {
activeItems[i].setAttribute("class", "item");
}
}
}
And have created a fiddle here -
https://jsfiddle.net/1opq1eyj/1/
Any advice on where i'm going wrong would be much appreciated.
Also would be grateful for any advice on how the script could have been improved upon.
Thanks,

You have wrong ordering
First you should deselect all and then you should make this.setAttribute
function fadeOutItems() {
for (i = 0; i < activeItems.length; i++) {
activeItems[i].setAttribute("class", "item fade-out");
this.setAttribute("class", "item fade-in");
}
}
update link

As mentioned, the fade-in attribute is set each time inside the loop. For the last item, that means that activeItems[i] will overwrite that last class-setting. The 'fade-in' setting can be placed outside the loop.
function fadeOutItems() {
for (i = 0; i < activeItems.length; i++) {
activeItems[i].setAttribute("class", "item fade-out");
}
this.setAttribute("class", "item fade-in");
}
As for the possible alternatives. The same can be done with pure css with a single selector:
.item-list:hover > .item:not(:hover) {
opacity: 0.6;
}
This sets the opacity for all .items inside the .item-list div, except the one being hovered. fiddle Option 1
However, since width of the container is 100%, the effect may trigger somewhat to soon, (depending on the total layout).
To be on the safe side, you can add an extra class to the container once one of the child items is hovered with JS (as long as CSS parent selectors are not in basic support) and base the css on that class:
JS:
let activeItems = document.querySelectorAll('.item'),
itemList = document.querySelector('.item-list');
for (i = 0; i < activeItems.length; i++) {
activeItems[i].addEventListener("mouseover", function(){itemList.classList.add('fade');}, false);
activeItems[i].addEventListener("mouseout", function(){itemList.classList.remove('fade');}, false);
}
CSS:
.fade:hover > .item:not(:hover) {
opacity: 0.6;
}
Fiddle option 2

Related

How to remove CSS classes from the classList of each div element, and assign a desired CSS class to a div element, in an array of div elements?

I have a set of HTML buttons programmed with JavaScript. There's a class assigned to an array of HTML div elements, and the buttons are supposed to change that class.
Here's the code for one of the buttons:
a4.addEventListener('click', function()
{
//makes sure all the other buttons aren't chosen
var button = document.getElementsByClassName('unchosen');
for (var i = 0; i < button.length; i++)
{
button[i].classList.remove('chosen');
}
a4.classList.add('chosen');
//is supposed to assign the new css class to every plate div element and get rid of the undesired classes
const plates = document.querySelectorAll(".assay1Plate", ".assay2Plate", ".assay3Plate", ".assay4Plate");
for (var i = 0; i < 35; i++)
{
if (plates[i].classList.contains == "assay1Plate")
{
plates[i].classList.remove("assay1Plate");
console.log("Removed assay 1 plate class due to assay 4 click");
}
if (plates[i].classList.contains == "assay2Plate")
{
plates[i].classList.remove("assay2Plate");
}
if (plates[i].classList.contains == "assay3Plate")
{
plates[i].classList.remove("assay3Plate");
}
plates[i].classList.add("assay4Plate");
//I was going to use this to remove undesired classes from the div elements... but it doesn't really work
/*
for(var k = 0; k < classes.length; k++)
{
if (plates[i].classList.contains(classes[k]) && classes[k] != "assay4Plate")
{
plates[i].classList.remove(classes[k]);
console.log("Classes removed save 4");
}
}
*/
}
});
Console.log shows that classes are being ADDED to the classList of the elements, but it doesn't seem like they can be removed once they've already been added. Clicking the first button changes their classes, clicking the second changes it again, clicking the third changes it again, and so on.... but then clicking the first or second buttons again does nothing.

Reference Individual Class Elements with 'this'

If I'm looping through different elements with the same class with mouseenter / mouseout events and I'm trying to incorporate the 'this' keyword so the JS only triggers on the element I'm hovering over. I can't get it to work though.
I've stripped out my attempts to use the 'this' keyword to make the code easier to read. How do I go about having it so that only the element being hovered over has the mouseenter and then mouseout events applied to it whilst looping through the elements?
I can't use a jQuery solution.
codepen pen: https://codepen.io/emilychews/pen/mMEEBw
Code is below:
JS
// declare variable for the CSS class
var menuItem = document.querySelectorAll('.menu-item');
//loop through CSS class to change background to red
function myMouseEnter() {
for (i = 0; i < menuItem.length; i++) {
menuItem[i].style.background = "red";
}
}
//loop through CSS class to change remove red background
function myMouseLeave() {
for (i = 0; i < menuItem.length; i++) {
menuItem[i].style.background = "none";
}
}
//event handler to add function on mouseenter
for (j = 0; j < menuItem.length; j++) {
menuItem[j].addEventListener('mouseenter', myMouseEnter, false)
}
//event handler to add function on mouseout
for (k = 0; k < menuItem.length; k++) { menuItem[k].addEventListener('mouseout', myMouseLeave, false)
}
CSS
.menu-item {padding: 10px;
font-family: arial;
}
HTML
<ul class="unclick--menuitems">
<li class="menu-item">About</li>
<li class="menu-item"><a href="//google.com">Projects</a</li>
<li class="menu-item">Contact</li>
</ul>
In your two functions, all you need to do is refer to this. In that context, this refers to the .menu-item event that you are currently hovering over.
Note that you'll also probably want to attach a handler for the <a> tag children, or else whenever you hover over them, the script will think you're leaving the <li>, and attempt to change the colours.
This can be done by checking the toElement and relatedTarget of the event in question, and then checking whether those are the parent <li> element.
All up, your code would look like this:
// declare variable for the CSS class
var menuItem = document.querySelectorAll('.menu-item');
// loop through CSS class to change background to red
function myMouseEnter() {
this.style.background = "red";
}
// loop through CSS class to change remove red background
function myMouseLeave() {
// prevent the 'mouseout' from affecting the <a> children
var e = event.toElement || event.relatedTarget;
if (e.parentNode == this || e == this) {
return;
}
this.style.background = "none";
}
// event handler to add function on mouseenter
for (j = 0; j < menuItem.length; j++) {
menuItem[j].addEventListener('mouseenter', myMouseEnter, false);
}
// event handler to add function on mouseout
for (k = 0; k < menuItem.length; k++) {
menuItem[k].addEventListener('mouseout', myMouseLeave, false);
}
.menu-item {
padding: 10px;
font-family: arial;
}
<ul class="unclick--menuitems">
<li class="menu-item">About</li>
<li class="menu-item">Projects</li>
<li class="menu-item">Contact</li>
</ul>
Note that the functions themselves don't have to loop through the menu items again ;)
Hope this helps! :)

Toggle is-visible Class to Div Next to Trigger Element (Plain JS)

This is supposed to be a very simple dropdown FAQ system, I know how to do this in jQuery but I want to learn plain JS.
I just want the individual clicked triggers to toggle the is-visible class to the content divs next to the clicked trigger. Like $(this).next addClass — just in JS.
I've really tried to search for this issue but 90% that shows up is how to do it in jQuery :-p
https://jsfiddle.net/48ea3ruz/
var allTriggers = document.querySelectorAll('.faq-trigger');
for (var i = 0; i < allTriggers.length; i++) {
// access to individual triggers:
var trigger = allTriggers[i];
}
var allContent = document.querySelectorAll('.faq-content');
for (var i = 0; i < allContent.length; i++) {
// access to individual content divs:
var content = allContent[i];
}
// I don't know how to target the faq-content div next to the clicked faq-trigger
this.addEventListener('click', function() {
content.classList.toggle('is-visible');
});
Would really appreciate some advice! :-)
Use nextSibling, when you are iterating .faq-trigger
var allTriggers = document.querySelectorAll('.faq-trigger');
for (var i = 0; i < allTriggers.length; i++) {
allTriggers[i].addEventListener('click', function() {
this.nextSibling.classList.toggle('is-visible');
});
}
nextSibling will also consider text-nodes, try nextElementSibling also
var allTriggers = document.querySelectorAll('.faq-trigger');
for (var i = 0; i < allTriggers.length; i++) {
allTriggers[i].addEventListener('click', function() {
this.nextElementSibling.classList.toggle('is-visible');
});
}

CSS selector for ALL elements with SAME CLASS after and before current element

I was looking here: CSS Selector for selecting an element that comes BEFORE another element?
...but wasn't able to find a correct answer for my issue.
Here is a fiddle example: https://jsfiddle.net/Munja/bm576q6j/3/
.test:hover, .test:hover + .test
With this, when I :hover element with .test class, I achieved to change style for current element with .test class and first next element with .test class.
What am I trying to achieve?
When I select any row/column (e.g agent 2), I want to apply same style for ALL elements with that same class (.test in this case).
If it is not possible to achieve this with css only, * I am willing to accept and other good solution.*
Thank you.
In your specific case you can use
tbody:hover > .test {
background: green;
}
fiddle: https://jsfiddle.net/bm576q6j/4/
Note that if you add more classes in the same tbody it will not give what you want. Check also this question: Hover on element and highlight all elements with the same class
So, after waiting for several more hours, I have decided to use JavaScript solution mentioned in answer from #BasvanStein. Posting it here as answer, to make things easier for someone else with same issue.
Here is a working fiddle: https://jsfiddle.net/Munja/bm576q6j/15/
var elms = document.getElementsByClassName("test");
var n = elms.length;
function changeColor(color) {
for(var i = 0; i < n; i ++) {
elms[i].style.backgroundColor = color;
}
}
for(var i = 0; i < n; i ++) {
elms[i].onmouseover = function() {
changeColor("red");
};
elms[i].onmouseout = function() {
changeColor("white");
};
}

How to change menu li class when div changes

I'm working on a one-page site that has several "articles". Each div has class "article" and id "first", "second", "third", etc. I have a standard menu:
<ul id="nav">
<li id="n1"></li>
<li id="n2"></li>
<li id="n3"></li>
//...etc
</ul>
What I need to do is assign the class "active" to the li tag w/id n1 when the current article has id "first" (and unassign active class from all other li tags); assign active class to li tag w/id n2 when current article has id "second"; etc.
I would appreciate any help... I'm really stuck on this one. TIA for your help.
P.S. I'm using the following code to, in essence, assign an active class to the currently viewed article:
$('#first, #second, #third, ...).bind('inview', function (event, visible) {
if (visible == true) {
$(this).addClass("inview");
} else {
$(this).removeClass("inview");
}
});
There are more ways to do it. This is one, which may give you ideas:
var navrefs= document.getElementById('nav').getElementsByTagName('a');
for (var i=0, len = navrefs.length;i<len;i++){
if (location.hash == navrefs[i].href){
navrefs[i].parentNode.className = 'active';
} else {
navrefs[i].parentNode.className = '';
}
}
Here's an example of the following →
The following assumes you have the class current on your currently active article page:
var articles = document.getElementsByClassName('article'),
al = articles.length,
which, i;
for (i = 0; i < al; i++) {
if (/current/.test(articles[i].className)) {
which = articles[i].id;
}
}
var atags = document.getElementsByTagName('a'),
al2 = atags.length;
for (i = 0; i < al2; i++) {
if (atags[i].href.search(which) > -1) {
atags[i].parentNode.className = 'active';
} else {
atags[i].parentNode.className = '';
}
}
Since it's to achieve a CSS effect, why not just right CSS like this:
.first #n1,
.second #n2,
.third #n3,
...etc
{
CSS for active article
}
and then just make the JS in charge of setting the appropriate class on the wrapper/body, e.g.
<li id="n1"></li>
etc.
...
function setC(str){
document.getElementById('wrapper').className=str;
}
This would would reduce the demand on the JS engine

Categories