Javascript - Script doesn't work on second click - javascript

A basic question by a newbie…
I always encounter the same problem :
I often want an element "A" to show an element "B" on click when "B" is hidden, but to hide it when it is visible. Here are two different try that doesn't work :
let ref = document.getElementsByTagName('sup');
let refContent = document.getElementsByTagName('i');
for(let i = 0; i < ref.length ; i++) {
ref[i].addEventListener('click', function() {
if (refContent[i].style.display == "inline") {
refContent[i].style.display = "none";
} else {
refContent[i].style.display = "none";
};
});
}
let ref = document.getElementsByTagName('sup');
let refContent = document.getElementsByTagName('i');
for(let i = 0; i < ref.length ; i++) {
if (refContent[i].style.display == "") {
ref[i].addEventListener('click', function() {
refContent[i].style.display = "inline";
});
} else {
ref[i].addEventListener('click', function() {
refContent[i].style.display = "none";
});
}
}

Hi
I'm gonna change some variable names to make this easier to talk about. So, you start off strong
// get all elements with tag sup
let buttons = document.getElementsByTagName('sup');
//get all elements with tag i
let showables = document.getElementsByTagName('i');
For each element buttons we want to bind an onclick event listener. showables should respond to those clicks. You write
//for every `button` bind this `event listener`
buttons[i].addEventListener('click', function() {
if (showable[i].style.display == "inline") {
showable[i].style.display = "none";
} else {
showable[i].style.display = "none";
};
});
}
To make this a little more clear lets take out the function and give it a name
let show_hide = function(i) {
if (showable[i].style.display == "inline") {
showable[i].style.display = "none";
} else {
showable[i].style.display = "none";
};
});
//for each button, bind show_hide
for(let i = 0; i < buttons.length ; i++) {
buttons[i].addEventListener('click', show_hide(i))
}
The loop there does exactly what you'd expect; it binds show_hide to each button.
What does show_hide do?
//if the element is shown, hide it
if (showable[i].style.display == "inline") {
showable[i].style.display = "none";
//if the element is not shown, hide it
} else {
showable[i].style.display = "none";
};
as you can see that no matter what, show_hide hides the element!
so a quick change will fix this for you.
if (showable[i].style.display == "inline") {
showable[i].style.display = "none";
//if not shown, show
else {
**** showable[i].style.display = "inline";
};
putting it all together, this should work
let show_hide = function(i) {
if (showable[i].style.display == "inline") {
showable[i].style.display = "none";
} else {
showable[i].style.display = "inline";
};
});
//for each button, bind show_hide
for(let i = 0; i < buttons.length ; i++) {
buttons[i].addEventListener('click', show_hide(i))
}
If you want my solution to this problem I'd be more than willing to solve this problem in my own way. Comment and let me know! I hope this helps
fin

I will interpret what you have written but I do not have the complete code I will fix some issues and update if/when you update your code by adding the markup (html).
You should put your selected elements in an array because some browsers will not let you loop over a NodeLists. You can change a NodeLists into an array in a few ways I like the Array.From() way. After changing the NodeLists into an array you can safely loop over the elements in the array using a forEach() loop.
let ref = Array.from(document.getElementsByTagName('sup'));
let refContent = Array.from(document.getElementsByTagName('i'));
ref.forEach(el => {
el.addEventListener('click', e => {
e.target.getElementsByTagName('i').style.display = '' // do something
});
});
This snippet will most likely not work as-is, I'm missing information to finish this script, but perhaps this is a good starting point for further exploration. (Search for stuff and add 'mdn', to get good results).
In stead of directly changing the style of an element it is better to either add or remove a css class which contains the css properties you desire. You can also toggle a class: element.toggle('className).

Related

Remove inline styles when clicking on button

The following happens to me:
I have made a slider with the next and previous arrows that works correctly. The case is that I have a button called "see all" in which when I press it, it puts a display none to the previous and next arrows and shows everything that is in the slider (that is to say, when I press the button, it shows everything and "deactivates" the slider).
The problem is that when you press the button again to stop displaying everything and return to "slider mode", the previous and next buttons do not go through the slider.
The slider is putting the active class and removing it to show what is inside. But once I have used the button of "see all" it is added in line a "display: none" and although I give to the arrows of previous or following and the class active is put correctly it remains the style="display:none;" inline in the html and it stops working.
The code of the button that shows everything:
var btnMore = document.querySelector(".btn-panes");
var boxes = document.querySelectorAll("#slider .box-panes");
var aLeft = document.querySelector("#slider .left");
var aRight = document.querySelector("#slider .right");
var btnMoreActivated = false;
btnMore.addEventListener("click", function(){
if(!btnMoreActivated){
for(var i=0; i<boxes.length; i++ ){
document.querySelector("#slider").style.flexWrap = "wrap";
boxes[i].style.display = "flex";
aLeft.classList.add("ocultar");
aRight.classList.add("ocultar");
}
btnMoreActivated = true;
} else {
for(var i=1; i<boxes.length; i++ ){
boxes[i].style.display = "none";
document.querySelector("#slider").style.flexFlow = "nowrap";
aLeft.classList.remove("ocultar");
aRight.classList.remove("ocultar");
}
btnMoreActivated = false;
}
});
The slider code (it works) but just to show you what it does in case you need to add something to fix the problem:
const items = document.querySelectorAll('#slider .box-panes');
const itemCount = items.length;
const nextItem = document.querySelectorAll('.right img');
const previousItem = document.querySelectorAll('.left img');
var count = 0;
function shorHide(){
switch (key) {
case value:
break;
default:
break;
}
}
function showNextItem() {
items[count].classList.remove('active');
if(count < itemCount - 1) {
count++;
} else {
count = 0;
}
items[count].classList.add('active');
}
function showPreviousItem() {
items[count].classList.remove('active');
if(count > 0) {
count--;
} else {
count = itemCount - 1;
}
items[count].classList.add('active');
}
function keyPress(e) {
e = e || window.event;
if (e.keyCode == '37') {
showPreviousItem();
} else if (e.keyCode == '39') {
showNextItem();
}
}
nextItem[0].addEventListener('click', showNextItem);
previousItem[0].addEventListener('click', showPreviousItem);
document.addEventListener('keydown', keyPress);
This is because inline styles are the most specific type of style to add and therefore the most difficult to override. The simplest solution is to not use inline styles and instead use a class that can be added or removed as needed.
Here's a simple example that you can use to replace: boxes[i].style.display = "none";
// Get the elements that need to be hidden/shown into a collection
let btns = document.querySelectorAll(".nav");
document.getElementById("hide").addEventListener("click", function(e){
// Loop over the collection
btns.forEach(function(el){
el.classList.add("hidden"); // Apply the hidden class
});
});
document.getElementById("show").addEventListener("click", function(e){
btns.forEach(function(el){
el.classList.remove("hidden"); // Remove the class
});
});
.hidden { display:none; }
<button id="hide">Hide Buttons</button>
<button id="show">Show Buttons</button>
<button class="nav"><<</button> <button class="nav">>></button>

How to stop .className when click anywhere on the screen

I have a function that makes an element from a list of elements change its .className when clicked, so lets say when I click the element becomes one color and the others another color. This function is the following:
const memberB = document.querySelectorAll('#memberBoxAlex,
#memberBoxLiv, #memberBoxFlo');
for (let i = 0; i < memberB.length; i++)
memberB[i].onclick = function(){
memberBoxAlex.className = "faded";
memberBoxLiv.className = "faded";
memberBoxFlo.className = "faded";
if(memberB[i].className=="open"){
memberB[i].className="";
}
else{
memberB[i].className="open";
}
This works perfectly, but what I want to happen next, is when I click outside its box to stop the all the effects so to make all memberB "normal" let's say, so to have .className="". I've tried to give to their container this function:
let exitEffect = document.getElementById(team)
exitEffect.onclick = function(){
memberBoxAlex.className = "";
memberBoxLiv.className = "";
memberBoxFlo.className = "";}
How can I do so when I click outside the box of the member all className for memberB will "stop" or become .className="".
use a single class for this for a more generic selector and I use this snippet to use a single event listener for this.
window.addEvent = (event_type, target, callback) => {
document.addEventListener(event_type, function (event) {
// If the event doesn't have a target
// Or the target doesn't look like a DOM element (no matches method
// Bail from the listener
if (event.target && typeof (event.target.matches) === 'function') {
if (!event.target.matches(target)) {
// If the element triggering the event is contained in the selector
// Copy the event and trigger it on the right target (keep original in case)
if (event.target.closest(target)) {
const new_event = new CustomEvent(event.type, event);
new_event.data = { originalTarget: event.target };
event.target.closest(target).dispatchEvent(new_event);
}
} else {
callback(event);
}
}
});
};
and then
window.addEvent('click', '.openable-member', (event) => {
document.querySelectorAll('.openable-member').each((element) => {
if (element !== event.target) {
element.classList.add('faded');
element.classList.remove('open'); // guessing you'll need this too
}
});
event.target.classList.toggle('open');
});
The Document method querySelectorAll() returns a static (not live) NodeList representing a list of the document's elements that match the specified group of selectors.
So you can map through memberB because it's not an array.
What you can do is:
const memberB = document.querySelectorAll('#memberA,#memberAA, #memberAAA ');
memberB.onclick = function(){
memberB.className = "faded";
if(memberB.className == "open"){
memberB.className = "";
}
else{
memberB.className = "open";
}
}
You can try this:
memberB[i].className = memberB[i].className.replace("open", "");

Why does my JS 'click event' only run once?

I created a click event that opens a previously 'hidden' div and closes it again once you click the same button.
However, it only runs once (one open and one close) - I'm at a loss to explain why it doesn't work if I click it again.
let readMore = document.getElementById('clickAbout');
let moreInfo = document.getElementById('about');
let changeSepa = document.getElementById('sepChange');
readMore.addEventListener('click', function(){
changeSepa.style.height = '2rem';
if (moreInfo.className == "") {
moreInfo.className = "open";
moreInfo.style.display = 'block';
} else {
moreInfo.style.display = 'none';
}
});
this happens because you're checking if className == "", but you are modifying the className to be "open". On the second click it checks the className which is now "open" and goes to the else block. On the third click you expect for it to go into the first block but the className is still "open".
For an easy fix just change the className in the else block
else {
moreInfo.className = "";
moreInfo.style.display = 'none';
}
Also i suggest you make use of the classList property on elements
https://developer.mozilla.org/en-US/docs/Web/API/Element/classList
using the class list it could look like this:
readMore.addEventListener("click", function () {
changeSepa.style.height = "2rem";
if (moreInfo.className == "") {
moreInfo.classList.add("open");
moreInfo.style.display = "block";
} else {
moreInfo.classList.remove("open");
moreInfo.style.display = "none";
}
});
Or even
readMore.addEventListener("click", function () {
changeSepa.style.height = "2rem";
moreInfo.classList.toggle("open");
if (moreInfo.className == "") {
moreInfo.style.display = "block";
} else {
moreInfo.style.display = "none";
}
});

Javascript show div on HTML select

I've been playing with javascript to create a drop down list that shows a div depending on which option is selected.
All the code can be seen here:
http://jsfiddle.net/nmdTy/
var select = document.getElementById('test'),
onChange = function(event) {
var shown = this.options[this.selectedIndex].value == 1;
document.getElementById('hidden_div').style.display = shown ? 'block' : 'none';
};
I want to know how do I streamline this code and remove repetition - maybe some kind of loop?
Another code :
var select = document.getElementById('test'),
nbItems = 2,
onChange = function (event) {
var val = this.options[this.selectedIndex].value;
for (var i = 1; i <= nbItems; i++) {
document.getElementById('hidden_div' + i).style.display = val == i ? 'block' : 'none';
}
};
http://jsfiddle.net/nmdTy/11/
You don't need two event handlers, you can use variables (shown below) to determine which div needs to be displayed or hidden.
var select = document.getElementById('test'), onChange = function(event) {
var div1 = 'hidden_div';
var div2 = 'hidden_div2';
var index1 = this.options[this.selectedIndex].value == 1;
var index2 = this.options[this.selectedIndex].value == 2;
if(index1 || index2){
document.getElementById(div1).style.display = index1 ? 'block' : 'none';
document.getElementById(div2).style.display = index2 ? 'block' : 'none';
}
else{
document.getElementById(div1).style.display = 'none';
document.getElementById(div2).style.display = 'none';
}
};
// attach event handler
if (window.addEventListener) {
select.addEventListener('change', onChange, false);
} else {
// of course, IE < 9 needs special treatment
select.attachEvent('onchange', function() {
onChange.apply(select, arguments);
});
}
Working Fiddle
I'm not really sure what do you mean by "repetition" but my guess is, that you don't want to type every each of the divs to be hidden/shown.
There could be multiple approaches to such task. The most universal is to have the div id's in a separate array. Then you can hide all but the selected div.
var divs = ["hidden_div1", "special_hidden", "one_more_hidden"];
var select = document.getElementById('test');
var onchange = function(event) { //Use var!
var shown = this.options[this.selectedIndex].value;
for(var i=0; i<window.divs.length; i++) { //It would be more effective to save last shown div in a variable, but I've chosen this aproach with loop
var div = document.getElementById(window.divs[i]);
if(div!=null) {
if(i==shown)
div.style.display="block";
else
div.style.display="none";
}
}
};
select.addEventListener("change", onchange); //Could type the function right here, without using "onchange" variable
In my code, <option> value represents index in the array. Here is jsFiddle.
Delegating a change event in IE<9 is a pain. It is possible, check this question to see how it's done, but it's not what you call elegant.
But your code doesn't delegate the event, so just attaching the handler directly at the onload event should do the trick (and it's X-browser compatible):
document.getElementById('test').onchange = function(e)
{
e = e || window.event;//the only IE headache
var shown = this.options[this.selectedIndex].value == 1;
document.getElementById('hidden_div').style.display = shown ? 'block' : 'none';
//^^ could keep a reference to this in a closure
};
The full code (with onload and closure reference to hidden div and preventing memory leaks in ie) should look like this:
var winLoad = function(e)
{
var hiddenDiv = document.getElementById('hidden_div');
document.getElementById('test').onchange = function(e)
{
var shown = !!(this.option[this.selectedIndex].value == 1);//to be safe, coerce to bool
hiddenDiv.style.display = shown ? 'block' : 'none';
};
if (window.addEventListener)
{
return window.removeEventListener('load',winLoad,false);
}
return window.detachEvent('onload',winLoad);
};
if (window.addEventListener)
{
window.addEventListener('load',winLoad,false);
}
else
{
window.attachEvent('onload',winLoad);
}
that should work fine on all major browsers, even IE7 (probably IE6, too)

How to write custom event which gets fired when user click three times

i am trying to write a custom event which should get fire when user click three times on any html node.
i know that i can create even using
var evt = document.createEvent("Event");
evt.initEvent("myEvent",true,true);
but i am not getting how i will capture that three times click event.
I will be appreciated if some one can suggest me the write approach for this.
Thanks!!!
You can create a special event
Code and example - here is your problem solvation :)
Just create a variable that stores the number of clicks.
var clickTimes = 0;
element.addEventListener('click', function(event) {
clickTimes++;
if(clickTimes==3) {
clickTimes = 0;
/* do something like dispatch my custom event */
}
});
This will count the clicks for any specific element and trigger Event on every third click.
$('selector').on('click',function(e){
Event_threshold = 500;
var clicked_times = $(this).data('Event-clicked-times');
if(clicked_times == '')
clicked_times = 0;
if(clicked_times == 0)
$(this).data('Event-first-click-timestamp',e.timeStamp);
clicked_times++;
if(e.timeStamp-$(this).data('Event-first-click-timestamp')<Event_threshold)
{
if(clicked_times == 3)
{
$(this).data('Event-clicked-times',0);
$(this).trigger('Event');
}
else
$(this).data('Event-clicked-times',clicked_times);
}
else
$(this).data('Event-clicked-times',0);
});
EDIT:
Fixed and added threshold control.
You can create iteration variable and check if element was three times clicked.
For example:
var clickTimer = 0;
document.body.addEventListener('click', function() {
clickTimer++;
if(clickTimer == 3) {
clickTimer = 0;
// fire your event
}
}, true);
To make this behavior like dbclick you can compare timestamp with first click.
For example:
var clickTimes = 0;
var fisrtClickTime = 0;
element.addEventListener('click', function(event) {
clickTimes++;
if(clickTimes == 1) {
fisrtClickTime = +new Date();
}
if(clickTimes == 3) {
clickTimes = 0;
firstClickTime = 0;
if((+new Date() - fisrtClickTime) < 1000) {
/* do something like dispatch my custom event */
}
}
});
This works without using external variables, using the HTML5 "data-" attribute for storage, so you will work on multiple elements.
$('#yourLink').click(function() {
window.setTimeout(function() {$(this).data("count",1)},300)
if(typeof $(this).data("count")=='undefined') {
$(this).data("count",1)
}
else {
var myCount = parseInt($(this).data("count"))
myCount++
if(myCount==3) {
alert("3!")
$(this).data("count",0)
}
else {
$(this).data("count",myCount)
}
}
})

Categories

Resources