How to simplify javascript event listeners? - javascript

What I want to achieve is, when hovering over the li with id="a", the corresponding ul with id="aa" appears. I have this html code:
<ul>
<li id="1">Option1</li>
<li id="2">Option2</li>
</ul>
<ul id="11">
<li>Option1.1</li>
<li>Option1.2</li>
</ul>
<ul id="22">
<li>Option2.1</li>
<li>Option2.2</li>
</ul>
and this javascript code:
for (i=1; i<3; i++) {
var fn = (function(i) {
var li = document.getElementById(i);
var ul = document.getElementById("" + i + i);
li.addEventListener("mouseover", function() {
ul.style.opacity = "1";
}, false);
})(i);
}
and it works as expected, but here the user Jordan Gray stated that it is possible to get rid of the loop and instead create one event listener for all list items - that is what I would like to achieve here. Unfortunately I do not understand his code and would be thankful if anyone could explain it to me or suggest a solution.

Here is how you can use that logic in your code:
var ul = document.getElementById('ul-id');
ul.addEventListener('mouseover', function(e) {
if (e.target.nodeName.toUpperCase() !== "LI") return;
document.getElementById("" + e.target.id + e.target.id).style.opacity = "0.6";
});
<ul id="ul-id">
<li id="1">Option1</li>
<li id="2">Option2</li>
</ul>
<ul id="11">
<li>Option1.1</li>
<li>Option1.2</li>
</ul>
<ul id="22">
<li>Option2.1</li>
<li>Option2.2</li>
</ul>

Await hovering a LI, Then display its corresponding element:
window.addEventListener("mouseover",function(e){
var el = e.target;
if(el.id && parseInt(el.id) < 10){
document.getElementById(el.id*11).style.opacity = 0.7;
}
});

You can add an event listener for the parent ul and ase the target element in this listener in order to know on which li it triggered.
ctn.addEventListener('mouseover', function(event) {
// eventually safeguard it with: if (event.target.tagName !== 'LI') return
id = event.target.id.repeat(2)
document.getElementById(id).style.opacity = 1
}, false);
.passive {
opacity: 0;
}
<ul id="ctn">
<li id="a">Option1</li>
<li id="b">Option2</li>
</ul>
<ul id="aa" class="passive">
<li>Option1.1</li>
<li>Option1.2</li>
</ul>
<ul id="bb" class="passive">
<li>Option2.1</li>
<li>Option2.2</li>
</ul>

Related

Javascript: Toggle div visibility in two steps

This question maybe stupid for many here. I have a bunch of divs and want to make them appear/disappear on click with special behaviour:
On-load state: all divs visible
click: all divs disappear, except for the one that was selected when clicking
to n-th click: toggle visibility for the div that was selected when clicking
What I've got so far:
function toggle_visibility(id) {
var e = document.getElementById(id);
if(e.style.display == 'block')
e.style.display = 'none';
else
e.style.display = 'block';
}
function toggle_class(id) {
var thisElem = document.getElementById(id);
var invisible = "invisible";
var visible = "visible";
var classes = thisElem.classList;
if (classes == invisible) {
thisElem.className = thisElem.className.replace(invisible, visible);
}
else {
thisElem.className = thisElem.className.replace(visible, invisible);
}
}
<ul id='list'>
<li id='1-i' class='visible'>Toggle DIV #1</li>
<li id='2-i' class='visible'>Toggle DIV #2</li>
<li id='3-i' class='visible'>Toggle DIV #3</li>
<li id='4-i' class='visible'>Toggle DIV #4</li>
</ul>
<div id='1' style='display: block;'><h3>DIV #1</h3></div>
<div id='2' style='display: block;'><h3>DIV #2</h3></div>
<div id='3' style='display: block;'><h3>DIV #3</h3></div>
<div id='4' style='display: block;'><h3>DIV #4</h3></div>
This code shows all divs on page-load, then toggles visibility for the selected div on click. So on first click only the selected div will disappear with all others staying still visible - the opposite of what I want. Though from second click on, this behaviour is the desired one.
I have found similar other threads (like this one), but their issues seem to add complexity I'd like to avoid.
Thanks a lot for your help!
Edit:
Now I tried to update function toggle_class(id) { following Arun P Johny's example:
var firstrun = true;
function toggle_class(id) {
var thisElem = document.getElementById(id);
var invisible = "invisible";
var visible = "visible";
if (thisElem.className == 'invisible' && !firstrun) {
thisElem.className = thisElem.className.replace(invisible, visible);
} else {
thisElem.className = thisElem.className.replace(visible, invisible);
}
if (firstrun) {
var children = document.getElementsByClassName('visible');
for (var i = 0; i < children.length; i++) {
if (children[i].id != id) {
children[i].className = thisElem.className.replace(visible, invisible);
}
}
}
firstrun = false;
}
The result is somewhat confusing: On first click, the selected element changes its class to invisible (which I do understand, since the script tries to replace the class visible with invisible for all elements). So this is not the behaviour I want, the clicked element is supposed to keep the class visible (until it is clicked again, since this is when the div disappears).
And the even more confusing part to me: Not all other elements change their class to invisible, but only every second element.
What did I do wrong?
Overly simplified version: http://jsfiddle.net/9ref3cf2/
HTML
<ul id='list'>
<li data-id="1">Toggle DIV #1</li>
<li data-id="2">Toggle DIV #2</li>
<li data-id="3">Toggle DIV #3</li>
<li data-id='4'>Toggle DIV #4</li>
</ul>
<div data-id='1' class="listBlock"><h3>DIV #1</h3></div>
<div data-id='2' class="listBlock"><h3>DIV #2</h3></div>
<div data-id='3' class="listBlock"><h3>DIV #3</h3></div>
<div data-id='4' class="listBlock"><h3>DIV #4</h3></div>
JavaScript
function toggleListBlocks() {
$('.listBlock').hide();
$('.listBlock[data-id='+ $(this).data('id') +']').show();
}
$(document).ready(function(){
$('#list>li').click(toggleListBlocks);
});
One approach is to assign a class to all the toggled div elements and use them to fetch and hide them
<div id='1' class="toggle" style='display: block;'><h3>DIV #1</h3></div>
<div id='2' class="toggle" style='display: block;'><h3>DIV #2</h3></div>
<div id='3' class="toggle" style='display: block;'><h3>DIV #3</h3></div>
<div id='4' class="toggle" style='display: block;'><h3>DIV #4</h3></div>
then
var first = true;
function toggle_visibility(id) {
var e = document.getElementById(id), toggle;
if (e.style.display == 'block' && !first) {
e.style.display = 'none';
} else {
e.style.display = 'block';
var children = document.getElementsByClassName('toggle');
for(var i = 0; i<children.length; i++){
if(children[i].id != id){
children[i].style.display = 'none';
}
}
}
first = false;
}
Demo: Fiddle
Using classList, you may have to include a shim to support older browsers
<div id='1' class="toggle"><h3>DIV #1</h3></div>
<div id='2' class="toggle"><h3>DIV #2</h3></div>
<div id='3' class="toggle"><h3>DIV #3</h3></div>
<div id='4' class="toggle"><h3>DIV #4</h3></div>
then
var first = true;
function toggle_visibility(id) {
var thisElem = document.getElementById(id);
var classes = thisElem.classList;
if (classes.contains('hidden') || first) {
classes.remove('hidden');
var children = document.getElementsByClassName('toggle');
for (var i = 0; i < children.length; i++) {
if (children[i].id != id) {
children[i].classList.add('hidden');
console.log('x')
}
}
} else {
classes.add('hidden');
}
first = false;
}
Demo: Fiddle

element.onclick event after window load not working in javascript

I need help. I want elements to be shown when clicking on their siblings. I want to do this stuff with pure javascript.
When I run my code and I click on the neither nothing happens nor errors are shown in the browser console (Chrome and Firefox).
I think one problem could be the onclick event inside de window.onload function, but I don't find the way to fix it.
Here's my HTML code:
<div class="year_element">
<h3 class="year">2015</h3>
<ul class="hide_months months">
<li>Marzo
</li>
</ul>
</div>
<div class="year_element">
<h3 class="year">1998</h3>
<ul class="hide_months months">
<li>Mayo
</li>
</ul>
</div>
<div class="year_element">
<h3 class="year">1987</h3>
<ul class="hide_months months">
</ul>
</div>
And here's my JavaScript code:
window.onload = function() {
var years = document.getElementsByClassName('year');
//When doing click on a year, show months
for (var i = 0; i < years.length; i += 1) {
//Function needs event as parameter to work
years[i].onclick = function(event) {
var selectedYear = event.target;
var month_list = selectedYear.nextSibling.nextSibling;
//Showing months
if (month_list.classList.contains('hide_months')) {
month_list.classList.remove('hide_months');
//Hiding months
} else {
month_list.classList.add('hide_months');
}
};
};
}
Your answers telling me the code worked, gave me the key of the problem: another file js was executing a window.onload function so this one didn't work. This has been fixed by using.
window.addEventListener("load", one_function, false);
window.addEventListener("load", another_function, false);
Thanks.
Are you running your javascript correctly?
I pasted everything into a html file, and the click seems to work.
It adds and removes the hide_months class. I don't have your css to make Marzo/Mayo dissapear, but looks like the loading and clicking are working as expected.
<script type="text/javascript">
window.onload = function() {
var years = document.getElementsByClassName('year');
//When doing click on a year, show months
for (var i = 0; i < years.length; i += 1) {
console.log('found some elements');
//Function needs event as parameter to work
years[i].onclick = function(event) {
var selectedYear = event.target;
var month_list = selectedYear.nextSibling.nextSibling;
//Showing months
if (month_list.classList.contains('hide_months')) {
month_list.classList.remove('hide_months');
//Hiding months
} else {
month_list.classList.add('hide_months');
}
};
};
}
</script>
Hm I try your example and it work fine in chroma.
I just add CSS that should work for this purpose.
<html>
<body>
<div class="year_element">
<h3 class="year">2015</h3>
<ul class="hide_months months">
<li>Marzo
</li>
</ul>
</div>
<div class="year_element">
<h3 class="year">1998</h3>
<ul class="hide_months months">
<li>Mayo
</li>
</ul>
</div>
<div class="year_element">
<h3 class="year">1987</h3>
<ul class="hide_months months">
</ul>
</div>
<script>
window.onload = function() {
var years = document.getElementsByClassName('year');
//When doing click on a year, show months
for (var i = 0; i < years.length; i++) {
//Function needs event as parameter to work
years[i].onclick = function(event) {
var selectedYear = event.target;
var month_list = selectedYear.nextSibling.nextSibling;
//Showing months
if (month_list.classList.contains('hide_months')) {
month_list.classList.remove('hide_months');
//Hiding months
} else {
month_list.classList.add('hide_months');
}
};
};
}
</script>
<style>
.year_element{font-weight: normal;}
.year{font-weight: bold; cursor: pointer;}
.months{color: green;}
.hide_months{display: none;}
</style>
</body>
</html>

Add Class to Following Element

I had a look out on the interwebs for a jQuery image gallery and couldn't find one that suited what I wanted to do. So I, ended up creating one myself and am trying to figure out how to get the prev and next buttons to work.
<div class="gallery portrait">
<nav>
<div class="close"></div>
<div class="prev"></div>
<div class="next"></div>
</nav>
<div class="cover">
<img src="image.jpg">
</div>
<ul class="thumbs">
<li class="thumb">
<img src="image.jpg">
</li>
...
</ul>
</div>
I'm also using a bit of jQuery to add a class of .full to the .thumb a element, which makes the thumbnails go fullscreen.
$( ".thumb a" ).click(function() {
$( this ).toggleClass( "full" );
$( "nav" ).addClass( "show" );
});
Now I can't work out this next bit, I need a way when the .prev or .next buttons are clicked for it to remove the class of .full from the current element and add it to the next or previous .thumb a element, depending on which was clicked.
I've got a demo setup here: http://codepen.io/realph/pen/hjvBG
Any help is appreciated, thanks in advance!
P.S. If this turns out well, I plan on releasing it for free. I guess you can't have too many jQuery image galleries, eh?
You can use $.next() and $.prev():
$(".prev").click(function () {
var current = $('.full');
current.prev('.thumb').addClass('full');
current.removeClass('full');
return false; // stop propagation; prevents image click event
});
$(".next").click(function () {
var current = $('.full');
current.next('.thumb').addClass('full');
current.removeClass('full');
return false; // stop propagation; prevents image click event
});
I suggest the following additions to your code to handle wrapping around with your next and previous links:
$(".next").click(function (event) {
navigate("next");
return false;
});
$(".prev").click(function (event) {
navigate("prev");
return false;
});
function navigate(operation) {
var $thumbs = $(".thumb"),
$full = $thumbs.find("a.full").closest(".thumb"),
$next;
$thumbs.find('a').removeClass('full');
if (operation == 'prev' && $full.is($thumbs.first()))
$next = $thumbs.last();
else if (operation == 'next' && $full.is($thumbs.last()))
$next = $thumbs.first();
else
$next = $full[operation]();
$next.find('a').click();
}
Here is a forked CodePen.
Something like this will get you started, but what you're wanting to do takes a little time to get just right.
<script type="text/javascript">
var imgSrcs = ['/imgs/this.jpg', '/imgs/will.jpg', '/imgs/work.jpg', '/imgs/just.jpg', '/imgs/fine.jpg'];//img url loaded into an array
var btnPrev = document.getElementById('prev'),
btnNext = document.getElementById('next'),
cover = document.getElementById('cover'),
thumb = document.getElementById('thumb'),
currImgIx = 0;
btnPrev.onclick = function () {
if (currImgIx === 0) { return; };
currImgIx--;
cover.src = imgSrcs[currImg];
thumb.src = imgSrcs[currImgIx];
};
btnNext.onclick = function () {
if (currImgIx === imgSrcs.length - 1) { return; };
currImgIx++;
cover.src = imgSrcs[currImgIx];
thumb.src = imgSrcs[currImgIx];
};
</script>
<div class="gallery portrait">
<nav>
<div class="close">X</div>
<div id="prev" class="prev">Prev</div>
<div id="next" class="next">Next</div>
</nav>
<div class="cover">
<img id="cover" src="image.jpg">
</div>
<ul class="thumbs">
<li class="thumb">
<img id="thumb" src="image.jpg">
</li>
...
</ul>
</div>

javascript style.display not working

Anyone can tell me why even though by debugging with fireBug the script correctly finds the proper element, the style.display doesn't update the property of the ul which remains set to none?
<html>
<div id="nav">
<ul>
<li>Studio
</li>
</ul>
</div>
<div id="subnav1">
<ul style="display: none">
<li>normally hidden
</li>
</ul>
</div>
<script type="text/javascript" src="jquery-1.4.3.min.js"></script>
<script type="text/javascript">
function show()
{
var subNav1 = document.getElementById("subnav1");
var ull = subNav1.getElementsByTagName("ul");
for (var i = 0, ii = ull.length; i < ii; i++)
{
if(ull[i].style.display == "visible")
{
ull[i].style.display = "none";
}
else
{
ull[i].style.display = "visible";
}
}
};
</script>
</html>
"visible" is not a valid css display value. I think you are looking for "block"

In Javascript, is it possible to give tab an active style when it only unhides a div, instead of loading a new page?

I have Javascript tabs which show/hide divs instead of loading new pages. The tabs have a style which gives a hover effect. I now want to add an active style to match up with the curently visible div.
THE JAVASCRIPT, which does not work as it is from a version which loads pages:
function setActive() {
aObj = document.getElementById('nav').getElementsByTagName('a');
for(i=0;i < aObj.length;i++) {
if(document.location.href.indexOf(aObj[i].href) >= 0) {
aObj[i].className='active';
}
}
}
function showdiv(id){
document.getElementById(id).style.display = "block";
}
function hidediv(id){
document.getElementById(id).style.display = "none";
}
THE STYLE:
#pageAdmin { display:block; }
#userAdmin { display:none; }
THE HTML:
<ul id="nav">
<li><a onclick="showdiv('pageAdmin'); hidediv('userAdmin')"
href="#">Page Admin</a></li>
<li><a onclick="showdiv('userAdmin'); hidediv('pageAdmin')"
href="#">User Admin</a></li>
</ul>
<div id="pageAdmin">
<h1>Page admin</h1>
</div>
<div id="userAdmin">
<h1>User admin</h1>
</div>
This is my first question on SO, so I hope it is appropriate - please accept my apologies in advance if it is not!
Your setActive function isn't very helpful. And using location, href and hash it will be hard doing what you want.
You may change your script to
function showdiv(id){
var el = document.getElementById(id);
el.style.display = "block";
el.className = "active";
}
function hidediv(id){
var el = document.getElementById(id);
el.style.display = "none";
el.className = "";
}
Now it should do what you want.
However, you should take a look on jQuery.
Using jQuery you do eliminate the use of getElementById and you can much simpler attach an eventhandler for onclick's, as by using vanilla js. It is considered as bad practice to setup handlers in html attributes.
jQuery also handles you the splitting and joining the space separated className string, if you want to use multiple styles.
Using jQuery it looks like:
$("a", "#nav").click(function () {
var $navA = $(this);
var tabName = $navA.attr("data-tabName");
$(".tab").each(function () {
var $tab = $(this);
if ($tab.attr("id") === tabName) {
$tab.css({ display: 'block' }); // you may move this into active style
$tab.addClass("active");
$tab.removeClass("inactive");
}
else {
$tab.css({ display: 'none' }); // you may move this into inactive style
$tab.removeClass("active");
$tab.addClass("inactive");
}
});
});
The HTML for the jQuery example
<ul id="nav">
<li><a data-tabName="pageAdmin" href="#">Page Admin</a></li>
<li><a data-tabName="userAdmin" href="#">User Admin</a></li>
</ul>
<div class="tab" id="pageAdmin">
<h1>Page admin</h1>
</div>
<div class="tab" id="userAdmin">
<h1>User admin</h1>
</div>

Categories

Resources