Event fires in another event - javascript

On the first click on each section I create an li which contains the content of that section h2 and it has to be an anchor to that section. The problem is that item.onclick fires in the doSomething method instead of when I click the li.
How can I fix it?
PS: I know that the first child of each section is a h2.
var list = document.getElementById('cuprins');
var sections = document.getElementsByTagName('section');
var l = sections.length;
var viz = new Array(l).fill(0);
for(var i = 0; i < l; i++)
sections[i].addEventListener('click', doSomething);
function jump(h){
var top = h.offsetTop;
window.scrollTo(0, top);
console.log(h);
}
function doSomething(e){
var index;
for(var i = 0; i < l; i++)
if(e.currentTarget == sections[i]){
index = i;
break;
}
if(viz[index] == 0){
var text = sections[index].children[0].textContent;
var item = document.createElement('li');
item.innerHTML = text;
item.onclick = jump(sections[index]);
list.appendChild(item);
viz[index] = 1;
}
}

The problem is that jump(sections[index]); is not being assigned as the callback function for item.click. Instead it is being executed anytime doSomething is called and viz[index] == 0.
What then happens is that the return value from calling jump(sections[index]); is being assigned as the onclick of item, but jump doesn't return any value, so there is no function registered with onclick.
You need to have jump invoked when item is clicked, so you need the line to be:
item.onclick = function() { jump(sections[index]); };
So that the outer function is assigned as the event callback and the contents of the function aren't invoked until the event occurs.
Having said that, I would move away from onXyz event properties and move to modern, standards-based code with addEventListener():
item.addEventListener("click", function() { jump(sections[index]); });

You're executing function and assign the result to item.onlick event
Unstead, using the code below will assign function declaration to item.onclick event
if(viz[index] == 0){
var text = sections[index].textContent;
var item = document.createElement('li');
item.innerHTML = text;
item.onclick = function() {
jump(sections[index])
};
list.appendChild(item);
viz[index] = 1;
}
}

Related

.addEventListener - long list

I found few answer on my issue but probably I'm not so experienced to processes it to my case.
I have list of items generated to .html
<div id="grid">
by JavaScript
var div_block = "";
for (i = 0; i < 70; i++) {
div_block = div_block + '<div id="c' + i + '" class="card"></div>';
}
document.getElementById("grid").innerHTML = div_block;
for (i = 0; i < 70; i++) {
'var c' + i + ' = document.getElementById(c' + i + ');'
}
and it works fine.
I want to chose .addEventListner method to chose proper element but for 70 and more elements code:
c0.addEventListener("click", function () {revealCard(0);});
c1.addEventListener("click", function () {revealCard(1);});
...
cn.addEventListener("click", function () {revealCard(n);});
is huge and not elegant. Method I've tried didn't work
for (i = 0; i < 70; i++) {
'c'+i+'.addEventListener("click", function() { revealCard('+i+'); });'
}
How to build working addEventListener() for this?
Thanks a lot.
The problem you are facing can be solved by using the card class that you add on each of your card. Then, to refer to the right card, you can use the keyword this, which in the context of an addEventListener will refer to whichever DOM element received the click. You also won't need to generate a unique Id for each one of your div, which I think is a big plus.
Your code would look like this:
let div_block = "";
for (i = 0; i < 70; i++) {
div_block = div_block + '<div class="card"></div>';
}
const cards = querySelectorAll(".card");
cards.forEach(card => {
card.addEventListener("click", revealCard)
})
function revealCard(){
// Here, `this` refers to the card that was clicked
// So you can do what you want with it
console.log(this);
}
Slight modification to brk's answer, using a single event listener on the parent that will trigger for the events on the children
var div_block = "";
for (let i = 0; i < 5; i++) {
div_block += `<div data-attr="${i}" id="c${i}" class="card">Hello</div>`;
}
var grid = document.getElementById("grid");
grid.innerHTML = div_block;
grid.addEventListener('click', function (e) {
if (e.target.getAttribute('class') === 'card') {
revealCard(e.target.dataset.attr);
}
});
function revealCard(num) {
console.log(num)
}
<div id='grid'></div>
You can use dataset, that is while creating the dom add a dataset property.
Then use querySelectorAll to get all the div with class card and iterate over it to add event using addEventListener. On click of the element get the dataset value and pass to revealCard function
var div_block = "";
for (let i = 0; i < 5; i++) {
div_block += `<div data-attr="${i}" id="c${i}" class="card">Hello</div>`;
}
document.getElementById("grid").innerHTML = div_block;
document.querySelectorAll('.card').forEach(function(item) {
item.addEventListener('click', function(e) {
revealCard(item.dataset.attr)
})
})
function revealCard(num) {
console.log(num)
}
<div id='grid'></div>
There are multiple ways to do this, but I wouldn't use IDs and I wouldn't bind X event listeners. I would use event delegation and data-* attributes:
Build your list of elements:
const grid = document.getElementById("grid");
for (var i = 0; i < 70; i++) {
const child = document.createElement('div');
child.className = 'card';
child.dataset.index = i;
grid.appendChild(child);
}
Add an event listener to the grid element:
grid.addEventListener('click', function(event) {
let target = event.target;
// traverse up if clicked inside element
while (target.className !== 'card') {
target = target.parentNode;
}
if (target) {
revealCard(target.dataset.index);
}
});
const grid = document.getElementById("grid");
for (var i = 0; i < 70; i++) {
const child = document.createElement('div');
child.className = 'card';
child.dataset.index = i;
child.innerText = `Card ${i}`;
grid.appendChild(child);
}
grid.addEventListener('click', function(event) {
let target = event.target;
// traverse up if clicked inside element
while (target.className !== 'card') {
target = target.parentNode;
}
if (target) {
revealCard(target.dataset.index);
}
});
function revealCard(i) {
console.log(`Card ${i} revealed`);
}
<div id="grid"></div>
I found probably the easiest (shortest) solution:
for(var i=0;i<boardSize;i++){
document.getElementById('c'+i).addEventListener("click",function(){
revealCard(this.id.substring(1));
});
}
What do you thing?

I would dynamically add the onmouseover and onmouseout attributes to each anchor in my html page but it does not work

I would dynamically add the onmouseover and onmouseout attributes to each anchor in my html page when the page loads.
I tried in all the ways I know but I can not get it to work.
HTML
<body onload="init()">
JAVASCRIPT
function init(){
startTimers();
initLinksProperties();
}
function initLinksProperties(){
var onMouseOverColor = 'red';
var onMouseOutColor = '#00BFFF';
var links = document.getElementsByTagName("a");
for(var i = 0; i < links.length; i++){
links[i].onmouseover = function(){ cambiaColore(i, onMouseOverColor); };
links[i].onmouseout = function(){ cambiaColore(i, onMouseOutColor); };
}
}
function cambiaColore(index, color){
var element = document.links[index];
element.style.color = color;
}
I also tried with:
links[i].onmouseover = cambiaColore(i, onMouseOverColor);
links[i].onmouseout = cambiaColore(i, onMouseOutColor);
links[i].addEventListener('onmouseover', cambiaColore(i, onMouseOverColor));
links[i].addEventListener('onmouseout', cambiaColore(i, onMouseOutColor));
links[i].setAttribute('onmouseover', cambiaColore(i, onMouseOverColor));
links[i].setAttribute('onmouseout', cambiaColore(i, onMouseOutColor));
cambiaColore is a function that changes the color of the link.
I have no syntax errors but nothing happens.
Where am I wrong?
When trying your code I had "Cannot read property 'style' of undefined" error in this function :
function cambiaColore(index, color){
var element = document.links[index];
element.style.color = color;
}
seems like "document.links[index]" doesn't return your element.
So I change your function a bit, instead of having index as first argument you have the element itself.
function cambiaColore(element, color){
element.style.color = color;
}
And in mouseover and mouseout functions I pass the element as first argument :
function initLinksProperties(){
var onMouseOverColor = 'red';
var onMouseOutColor = '#00BFFF';
var links = document.getElementsByTagName("a");
for(var i = 0; i < links.length; i++){
links[i].onmouseover = function(e){ cambiaColore(e.target, onMouseOverColor); };
links[i].onmouseout = function(e){ cambiaColore(e.target, onMouseOutColor); };
}
}
entire code :
function init() {
startTimers();
initLinksProperties();
}
function initLinksProperties(){
var onMouseOverColor = 'red';
var onMouseOutColor = '#00BFFF';
var links = document.getElementsByTagName("a");
console.log(links);
for(var i = 0; i < links.length; i++){
links[i].onmouseover = function(e){ cambiaColore(e.target, onMouseOverColor); };
links[i].onmouseout = function(e){ cambiaColore(e.target, onMouseOutColor); };
}
}
function cambiaColore(element, color){
element.style.color = color;
}
document.links is not accessible in cambiaColore. Pass the element or the event so you can retrieve the target you want to change. Right now you're trying to change undefined.
(function(window, document, undefined)
{
initLinksProperties();
})(window, window.document);
function initLinksProperties(){
var onMouseOverColor = 'red';
var onMouseOutColor = '#00BFFF';
var links = document.getElementsByTagName("a");
for(var i = 0; i < links.length; i++){
links[i].addEventListener('mouseover',
function(event)
{
var element = event.target;
cambiaColore(element, onMouseOverColor);
}, false);
links[i].addEventListener('mouseout',
function(event)
{
var element = event.target;
cambiaColore(element, onMouseOutColor);
}, false);
}
}
function cambiaColore(element, color){
element.style.color = color;
}
Link 1
Link 2
Link 3
Link 4
Link 5
When the function cambiaColore() is called, the value of the i variable has changed because the loop already finished. You don't need to pass index or element as others have suggested. You just need to use event.target:
function cambiaColore(index, color) {
// no need to use index or element. You can remove it.
event.target.style.color = color;
}
Link for the complete solution: https://jsfiddle.net/h6xhajtd/
If you want to know more about event.target: https://developer.mozilla.org/en/docs/Web/API/Event/target

Eventlisteners impact?

What impact does eventlisteres have? Im talking about big numbers, here's an example:
There's only x amount of .marker at first
All children are added via JS when .marker is clicked - eventlistener
Each child does it's own thing which means each of them have their own eventlisteners
<!-- Final HTML of single .marker when it has been clicked -->
<div class="marker">
<div class="remove"></div>
<div class="change"></div>
<div class="add"></div>
<div class="drag"></div>
</div>
var count = 20 000;
for(i = 0; i < count; i++) {
var marker = document.createElement('div');
marker.className = 'marker';
someParentElement.appendChild(marker);
marker.click( function() {
//Create child elements
var remove = document.createElement('div');
remove.className = 'remove';
marker.appendChild(remove);
var change = document.createElement('div');
change.className = 'change';
marker.appendChild(change);
var add = document.createElement('div');
add.className = 'add';
marker.appendChild(add);
var drag = document.createElement('div');
drag.className = 'drag';
marker.appendChild(drag);
//Children eventlisteners
remove.click( function() {
//Do it's thing
});
change.click( function() {
//Do it's thing
});
add.click( function() {
//Do it's thing
});
drag.click( function() {
//Do it's thing
});
});
}
Please don't mind other things, e.g creating 20 000 elements programmatically. My question is this: what would be the impact of having all these eventlisteners with all this code in them? Does it even matter what or how much code is inside eventlistener as long as it hasn't been triggered?
Try using event delegation , single event handler. See switch , .is()
var count = 100;
for (i = 0; i < count; i++) {
var marker = document.createElement('div');
marker.className = 'marker';
marker.innerHTML = marker.className + " " + i;
document.body.appendChild(marker);
//Create child elements
var remove = document.createElement('div');
remove.className = 'remove';
remove.innerHTML = "remove" + i;
marker.appendChild(remove);
var change = document.createElement('div');
change.className = 'change';
change.innerHTML = "change" + i;
marker.appendChild(change);
var add = document.createElement('div');
add.className = 'add';
add.innerHTML = "add" + i;
marker.appendChild(add);
var drag = document.createElement('div');
drag.className = 'drag';
drag.innerHTML = "drag" + i;
marker.appendChild(drag);
//Create child elements
}
var check = function(args) {
alert(args.innerHTML.replace(/[^\d+]/g, ""))
}
var obj = {
remove: check,
change: check,
add: check,
drag: check
}
var message = function(name) {
console.log(name)
}
$("body").on("click", ".marker", function(event) {
var name = event.target.className;
switch (name) {
case "remove":
/* do stuff */
message(name);
break;
case "change":
/* do stuff */
message(name);
break;
case "add":
/* do stuff */
message(name);
break;
case "drag":
/* do stuff */
message(name);
break;
default:
/* do stuff */
alert(name);
break;
}
// utilizing `.is()`
if ($(event.target).is(".remove")) {
// do stuff
event.target.innerHTML += "clicked"
}
if ($(event.target).is(".change")) {
// do stuff
event.target.innerHTML += "clicked"
}
if ($(event.target).is(".add")) {
// do stuff
event.target.innerHTML += "clicked"
}
if ($(event.target).is(".drag")) {
// do stuff
event.target.innerHTML += "clicked"
}
if (!$(event.target).is(".marker")) {
// utilizing an object
obj[event.target.className](event.target)
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
Creating event handlers for so many things which are repeated feels like a waste of CPU cycles, plus memory to manage so many event listeners.
Instead, it would be preferrable to use event bubbling/delegation to listen to the click events from a parent node (as close an ancestor element as possible would be ideal) and see what element triggered the event and call the appropriate code accordingly.
This would be a one-time bind, and should also catch dynamically added elements if done right.
Examples with jQuery that are also explained quite well include the following
https://learn.jquery.com/events/event-delegation/
http://api.jquery.com/on/
Though you are not limited to just jQuery to implement this.
Hope that helps.

Running functions onclick. All buttons being clicked

I have many buttons that have class="clearSelect". I want these buttons the execute a function onclick. I am new to javascript and not quite sure why this is occurring but I think my functions are being executed instead of only executing onclick
The code below is what is calling all my other functions causing every button to be clicked.
code:
var buttons = document.getElementsByClassName("clearSelect"); // objects with class="clearSelect"
for (var i = 0; i < buttons.length; i++) {
var button = buttons[i];
// button.addEventListener("onclick", clearOrSelectAll(button.id));
button.onclick = clearOrSelectAll(button.id);
}
These are the functions being called:
function clearOrSelectAll(btn) {
var cleartab = clearButtonSet[btn];
var selecttab = selectButtonSet[btn];
// console.log("clicked!");
if (cleartab != null) {
getOSList(cleartab, false);
} else {
getOSList(selecttab, true);
}
}
function getOSList(tabVal, fate) {
var configTab = document.getElementById(tabVal);
var browserList = configTab.getElementsByClassName("browser_list");
// var idObjs = browserList.getElementsByTagName("li");
for (var h = 0; h < browserList.length; h++) {
var idObjs = browserList[h].getElementsByTagName("li");
// console.log(h);
// console.log(idObjs);
// select all
if (fate) {
for (var i = 0; i < idObjs.length; i++) {
if (configs[idObjs[i].id] == null) {
idObjs[i].className = "selected";
configs[idObjs[i].id] = config_dictionary[idObjs[i].id];
}
}
// clear all
} else {
for (var j = 0; j < idObjs.length; j++) {
if (configs[idObjs[j].id] == null) {
idObjs[j].className = "unselected";
delete configs[idObjs[j].id];
}
}
}
}
}
#Christopher was very close, but button.id should be this.id.
button.onclick = function() {
clearOrSelectAll(this.id);
}
The reason button.id doesn't work can be demonstrated with this code:
var buttons= document.getElementsByTagName('button');
for (var i = 0; i < buttons.length; i++) {
var button = buttons[i];
button.onclick = function() {
alert(button.id);
}
}
<button id="B1">Button 1</button>
<button id="B2">Button 2</button>
<button id="B3">Button 3</button>
Each button returns "B3," because that's the last object that the variable button is assigned to.
In your for loop when you attach the event to all of the buttons, you are calling the clearOrSelectAll function. You probably want to wrap it in an anonymous function to make sure it's only called when the event is fired.
// Non-ideal solution: see edit
button.onclick = function() {
clearOrSelectAll(button.id);
}
EDIT: It has been pointed out that the 'this' context variable will point to the element in question when an event handler is attached by means of the onclick property, or the addEventListener method. As such it would probably be cleaner (and easier to read) if you were to reference that instead of using 'button' as a closure and count on javascript engines to not optimize your loop too heavily (as that would mess with the value of 'button' at the time that the event is called.
button.onclick = function() {
clearOrSelectAll(this.id);
};

Assigning onmouse events with a loop to a div array - Javascript

I'm trying to assign onmouseover - onmouseout events to an array of divs with a loop.
I created the divs through a loop as well using a function parameter createDivs(x), x being number of divs and a bunch of this.property to assign styles.
Everything is working as expected, but assigning the mouse events through a loop with the divArray.Length object.
The script is the following:
Making the divs:
containers : {
create : function(containerCount){
var cArray = [this.c1Color,this.c2Color,this.c3Color];
var aCounter = 0;
divArray = [];
for (var i = 1; i <= containerCount; i++){
var c = document.createElement("div");
c.id = ("container"+i);
c.style.width = "100%";
c.style.height = (this.height) + "px";
c.style.backgroundColor = (cArray[aCounter]);
aCounter++;
document.body.appendChild(c);
divArray.push(c);
}
}
},
Assigning the Events:
events : {
on : function () {
var z = 1;
for (var i = 0; i < divArray.length; i++){
var cont = ("container" + z);
document.getElementById(divArray[i].id).onmouseover = function(){
gala.animate.openAnimation(cont);
}
document.getElementById(divArray[i].id).onmouseout = function(){
gala.animate.shrinkAnimation(cont);
}
console.log(cont);
z++;
}
}
The console show the array sort through the number of divs as expected, and the cont variable ++ increase to assign the id. However at the end, the event listeners are only applied to the last element of the array.
Btw the cont variable is just a placeholder for a parameter that passes too the animation method so it knows what div to animate, meaning animat.openAnimation(cont) cont = div name.
Looks like you need a new scope to keep the value of the cont variable constant inside the event handlers. I replaced the cont variable as it didn't really seem neccessary
events : {
on : function () {
for (var j = 0; j < divArray.length; j++){
(function(i) {
divArray[i].onmouseover = function(){
gala.animate.openAnimation("container" + (i+1));
}
divArray[i].onmouseout = function(){
gala.animate.shrinkAnimation("container" + (i+1));
}
})(j);
}
}

Categories