stopPropagation onclick not working in nested list - javascript

i have the following function to swap images on the click of a ul in a nested list, but it doesnt stop bubbling up the list..
function bimageswap (step) {
step.stopPropagation;
realstep = parseInt(step) + 1;
nextsteps = realstep + 1;
for (iss = nextsteps;iss <= 5; iss++) {
document.getElementById("step" + iss).className = 'step' + iss;
alert(iss);
}
document.getElementById("step" + realstep).className = 'step' + realstep + 'a';
/*$("#step2").css( 'background-image', 'images/adtl_prodimg/discs/step1_.png');*/
return false;
}
it is called like this:
<ul onclick='return bimageswap("4")'>
i tried the return because it is what i found in another answer but it still doesnt work. i would greatly appreciate any help thanks!

The stopPropagation method is in the event object, you can't call it on a string. You are also missing the parentheses, so it would just get the stopPropagation property from the string (which returns undefined) and discard it.
Send the event object from the event handler to the function:
<ul onclick="bimageswap(event, '4');">
Use the event object in the function:
function bimageswap(event, step) {
event.stopPropagation();
...

Related

Using .keydown() to return div id?

I'm not really sure if I'm phrasing this correctly, but what I'm trying to do is have a function return a div's id on a keydown event.
Say my HTML is:
<div class="pad" id="snare" data-key="97"></div>
<div class="pad" id="kick" data-key="98"></div>
Essentially I want my JS to do something like this:
function handlekeydown(e) {
console.log("data-key: " + e.keyCode);
if (e.keycode == $(".pad").data("key")) {
return this.id
}
}
I could write a switch statement with each keycode returning a variable with the same name as the id, but that would defeat the purpose of having it in my HTML.
How would I write a function to search a div for a data-key that would match the keycode and then return its id?
As per the question
How would I write a function to search a div for a data-key that would match the keycode?
You can do it by:
function handlekeydown(e) {
var id;
var element = $('[data-key="' + e.keyCode + '"]');
if (element.length) {
id = element[0].id;
}
}
But as mentioned above you can't return the id from the handler, you might wanna call a method from the event handler which uses id.
function processId(id) {
console.log(id);
}
function handlekeydown(e) {
var element = $('[data-key="' + e.keyCode + '"]');
if (element.length) {
processId(element[0].id);
}
}
A keydown event is a keyboard event.
A keydown event will only trigger on the currently activeElement of the document.
You need a completely different approach - on which I will come forth with a solution proposal if you decide to change your current approach with something that might actually work for a change.
p.s.: I will update this same reply.

fullcalendar.io - Holding Event in memory

I believe javascript is holding the event in memmory but I can't figure out where and how to get around it. maybe you guys can help. I have created a JSFiddle to demonstrate the problem
JSFIDDLE
So it happens when you update one event. Then you move to another event go to update that event. It brings the old event to the new event. Changes the start and end and everything. First event updates fine.
When you click event. These functions are fired.
function eventClick(calEvent){
$('#edit-event-title').val(calEvent.code);
$('#edit-event-description').val(calEvent.description);
$('#event-start-edit-dpick').data('DateTimePicker').date(calEvent.start).format('YYYY-MM-DD');
$('#event-start-time-edit-dpick').data('DateTimePicker').date(calEvent.start).format('HH:mm');
$('#event-end-edit-dpick').data('DateTimePicker').date(calEvent.end).format('YYYY-MM-DD');
$('#event-end-time-edit-dpick').data('DateTimePicker').date(calEvent.end).format('HH:mm');
$('#fc_edit').click();
}
function createUpdateEvent(calEvent, create) {
//create event
if(create){
// create the event
} else {
$(".antosubmit2").on("click", function() {
calEvent.code = $("#edit-event-title").val();
calEvent.title = $("#edit-event-title option:selected").html();
calEvent.description = $("#edit-event-description").val();
calEvent.start = moment($('#event-start-edit-dpick').data('DateTimePicker').date().format('YYYY-MM-DD') + ' ' +
$('#event-start-time-edit-dpick').data('DateTimePicker').date().format('HH:mm') +
$('#event-start-edit-dpick').data('DateTimePicker').date().format('Z'));
calEvent.end = moment($('#event-end-edit-dpick').data('DateTimePicker').date().format('YYYY-MM-DD') + ' ' +
$('#event-end-time-edit-dpick').data('DateTimePicker').date().format('HH:mm') +
$('#event-end-edit-dpick').data('DateTimePicker').date().format('Z'));
calendar.fullCalendar('updateEvent', calEvent);
$('.antoclose').click();
});
}
}
function recalcHeaderHours(event){
var currentday = moment(event.start).format("YYYY-MM-DD");
if (event.totalHours > 0) {
var prev = $("#dailytotal-"+currentday).text() || 0;
$("#dailytotal-"+currentday).text(+prev + +event.totalHours);
}
}
I hope you guys can assist. Thanks for your time :)
I fixed your issue.
The problem is that you put the on click event to the ".antosubmit2" in line 83. On first event open there will be 1 click on this class, but on the 2. event open the code put another click on it. If you open only one event 2 times, there will be no problem. But it you do it on different events, on the second open, 2 click fires on it. The newly added click use correct data, but the first click use the first event datas. This is your problem I guess.
The first click should be unbind from the ".antosubmit2".
Try to modify your code like this:
function createUpdateEvent(calEvent, create) {
//create event
if(create){
// create the event
} else {
$(".antosubmit2").unbind('click');
$(".antosubmit2").on("click", function() {...
You see the "unbind('click')" line, add it to the code and it will works.

addEventListener Shows same value for all events

All, I have been trying different things for few hours now but I couldn't get around this issue, I have this function which accepts the JSON -
I can createElement('a') : FINE
I can set all element properties such as href etc : FINE
I can appendChild(a) with required element tags : FINE
But I cannot set a.addEventListener("click", function () { alert("clicked - " + a.id) }, false); Everytime i click on any href, it shows the last value
function setTags(tagsJson) {
var obj = JSON.parse(tagsJson);
var a = null;
var linkText = null;
for (var i = 0; i < obj.length; i++) {
if (typeof (obj[i].Tag_name) === 'undefined' || obj[i].Tag_name === null) {
// DO NOTHING
}
else
{ a = document.createElement('a');
linkText = document.createTextNode(obj[i].Tag_name);
a.appendChild(linkText);
a.href = "#";
a.textContent = obj[i].Tag_name;
a.className = "badge badge-success";
document.getElementById("tags").appendChild(a);
// HERE IS THE ISSUE
a.addEventListener("click", function () { alert("clicked - " + a.textContent) }, false);
}
}
}
an example if JSON has the following values
{\"Tag_id\":12,\"Tag_name\":\"Aston Martin\"},{\"Tag_id\":13,\"Tag_name\":\"Cars\"}]"}
It should append unique text at textContent such as 'Aston Martin', 'Auston' and 'Cars' but on clicking on any item, alter is shown as Cars although Text in the link shows correct values.
As per the above screenshot, i can see the text values are fine, but addEventListener only shows last value on CLICK
I think I am missing the understanding of addEventListener?
How can I tackle this issue
Cheers
Instead of a.textContent use this.textContent
a.addEventListener("click", function () { alert("clicked - " + this.textContent) }, false);
By the time this event listener is executed, for-loop has already been executed till i < obj.length is satisfied, which is why a is always the final one.
The function will trigger only when you click on the item, so when you click it will take the variable a which is declared outside of your for loop , and the a preserve the last item from loop.
Instead of using the variable a use the the current context into the function by using the word this.
a.addEventListener("click", function () { alert("clicked - " + this.textContent) }, false);
It happens because of how you write the alert function:
a.addEventListener("click", function(){
alert("clicked - " + a.textContent); // <---- THIS LINE
}, false);
In the line, a.textContent will get the textContent value from a.
In your implementation, the value of a is changing every loop. So, the a value the function gets will be the a value from the last loop. Therefore, you will always get the last value of a only.
To counter that, you can use another way to call for a.textContent within the function.
There is two ways according to your current implementation:
One: Use event.target
a.addEventListener("click", function(event){
// event is an event object passed by the `addEventListener` function
// where event.target === the DOM element that activates the listener
alert("clicked - " + event.target.textContent);
}, false);
Two: use this
a.addEventListener("click", function(){
// `this` value === the element that activates this event listener.
alert("clicked - " + this.textContent);
}, false);
Working example
var json = "[{\"Tag_id\":12,\"Tag_name\":\"Aston Martin\"},{\"Tag_id\":13,\"Tag_name\":\"Cars\"}]";
setTags(json);
function setTags(tagsJson) {
var obj = JSON.parse(tagsJson);
var a = null;
var linkText = null;
for (var i = 0; i < obj.length; i++) {
if (typeof obj[i].Tag_name === 'undefined' || obj[i].Tag_name === null) {
// DO NOTHING
}
else
{ a = document.createElement('a');
linkText = document.createTextNode(obj[i].Tag_name);
a.appendChild(linkText);
a.href = "#";
a.textContent = obj[i].Tag_name;
a.className = "badge badge-success";
document.getElementById("tags").appendChild(a);
// HERE IS THE ISSUE
a.addEventListener("click", function () { alert("clicked - " + this.textContent) }, false);
}
}
}
a {
display: block;
}
<div id="tags"></div>
This is a typical misuse of closures.
You are declaring your a variable outside of your for loop and more importantly your click callback function. So as you iterate through, your a is overriden. Finally each time you call your click function, a will have his value set to the last value set during the for loop.
So in your case you will always have
a.textContent = obj[obj.length - 1].Tag_name;
To make it work as expected, you need to retrieve you <a> from the click event itself. This can be done like so
a.addEventListener("click", function (event) { // use event here to find your element
const a = document.getElementById(event.target.id); // This is the element that was clicked
alert("clicked - " + a.textContent)
}, false);
Cause the function binding after for-loop
for (var i = 0;i<10;i ++){
aLis[i].onclick = (function(num){
return function(){
alert(num)
};
})(i);
}
Look at this :
http://jsbin.com/liyeqamone/5/edit?html,js,console,output

Toggle Classname using document.getElementsByClassName - JavaScript

I have a left sidebar menu which has submenus, i want each menu item to toggle a classname "active" so the submenu will open i have CSS for it.
The thing is i am using document.getElementsByClassName to select and iterate all of the menu items and is only working for the first element, i have been searching and it has something to do with closures and i am trying different solutions but its not working.
i am making the function so i can use it to toggle a classname of another div and not the one clicked, in that case i use and ID.
var toggleClassname = function (otherDiv, sameDiv) {
var divToToggleClass;
//are we going to use ID and toggle the classname of another div ?
if (sameDiv) {
divToToggleClass = this;
} else {
divToToggleClass = document.getElementById(otherDiv);
}
console.log(divToToggleClass);
var className = divToToggleClass.className + ' ';
if (~className.indexOf(' active ')) {
divToToggleClass.className = className.replace(' active ', '');
} else {
divToToggleClass.className += ' active';
}
};
var MenuItemsArray = document.getElementsByClassName("classOfMyMenuItems");
for (var i = 0; i < subMenuItemsArray.length; i++) {
MenuItemsArray[i].addEventListener('click', function () { toggleClassname(null, true) }, false);
}
i have been trying using [].forEach.call or wrapping the function in another that returns the function, not working.
I am doing this in pure javascript, cant use the new .classList.toggle i would also use attachEvent to be more backwards compatible (old IE).
The problem is that within your toggleClassname() function this is not equal to the clicked element. It will actually be either undefined or window depending on whether your code is running in strict mode or not.
A click handler bound with addEventListener() will have this set to the clicked element, so within the following anonymous function:
function () { toggleClassname(null, true) }
...the value of this is the element in question. But then you call toggleClassname() and don't pass it a reference to the clicked element or set its this value. You can explicitly set it using .call():
function () { toggleClassname.call(this, null, true) }
Further reading:
this in JavaScript
.call()
This answer might help you:
addEventListener using for loop and passing values
Without going too deep into your code, I'd say if you try and make it
for (var i = 0; i < subMenuItemsArray.length; i++) {
(function () {
var k = i;
MenuItemsArray[k].addEventListener('click', function () { toggleClassname(null, true) }, false);
}()); // immediate invocation
}
That should work.

Recursive function causes other calls to fire multiple times

I'm working on a quiz game, wherein the user is presented with a quote and has to guess the author:
function startGame(quotes) {
askQuestion(quotes[0], 0, 0);
function askQuestion(quote, question, score) {
var q = "<span class='quo'>“</span><em>" + quote.quote + "</em><span class='quo'>”</span>";
$('.choice').css('visibility', 'visible');
$('#questions').html(q);
$('.choice').click(function(e){
$('.choice').css('visibility', 'hidden');
e.preventDefault();
var nextq = (question + 1);
var btntxt = (nextq < number_of_questions ? 'Next...' : 'Final results');
if ($(this).attr('data-author') === quote.author) {
score++;
$('#questions').html('<h1>Correct.</h1><a class="btn next">' + btntxt + '</a>');
document.getElementById('win').play();
} else {
$('#questions').html('<h1>Wrong.</h1><a class="btn next">' + btntxt + '</a>');
document.getElementById('lose').play();
}
$('#questions').append('<h4>Score: ' + score + '/' + nextq + '</h4>');
$('.next').on("click", function(){
question += 1;
if (question < number_of_questions) {
askQuestion(quotes[question], question, score);
} else {
tallyScore(score);
}
});
});
}
}
When a question is asked, the askQuestion() function is called again if fewer than 6 questions have been asked.
Everything works great, but I'm having issues with the sound effects. If a user gets an answer right and then an answer wrong, both the "win" and "lose" sound effects are played simultaneously.
My guess is that this has something to do with my recursively calling askQuestion() -- it seems like the entire "history" of the function is looped through. I was having a similar problem earlier — on correct answers, the score global variable was incremented by the number of previously correct answers (instead of just by one).
Any idea how I can fix that? Thanks!
Edit: As requested, here's a JSfiddle.
easy fix actually. you are re-attaching the click listener over and over, so just remove it each time it gets set.
change
$('.choice').click(function (e) {
to
$('.choice').off().click(function (e) {
http://jsfiddle.net/NADYM/
Every time askQuestion is called, you add an event handler to the html elements. So when you click on the .choice element, multiple events are run.
Try giving a unique id to all generated element and use that id to attach event handlers.

Categories