Click handler event listene - javascript

I've been searching all over the place and even if I have found the answer, it has not been delivered in terms I can understand. I'm playing around with this code on jsfiddle, trying to understand why this click handler is not working. I apologize if this is a useless post, just trying to make sense of it all. If anyone knows any good tutorials on how JavaScript code is rendered and how functions pass objects etc.. please, do link me! I've read the basics of how to write functions etc.. but understanding whats going on when the code is parsed, to me, is quite a different thing.
Here is the code I'm trying to get to work:
http://jsfiddle.net/UumUP/3144/
// Function to change the content of t2
function modifyText(evt) {
var thing = evt.target;
thing.firstChild.nodeValue = "four";
}
// add event listener to t
var el = document.getElementsByTagName("td");
for(i = 0; i < el.length; i++) {
el[i].addEventListener("click", modifyText(evt), false);
}

You are calling the function and passing the result of that call, rather than passing a reference of the function, do this instead:
el[i].addEventListener("click", modifyText, false);
http://jsfiddle.net/UumUP/3145/

el[i].addEventListener("click", modifyText(evt), false);
supposed to be
el[i].addEventListener("click", modifyText, false);
Check Fiddle

Related

Hide the password textbox and submit

This is a simple password checking function I messed around with for a little bit. I've tried a lot of different methods (including, but not limited to: .css(), .on('click'), .click(), .animate(), .show(), .hide(), .preventDefault() on the submit), put selectors into variables, moved around all sorts of IDs and $('input[name="s"]') and all sorts of selectors. Not sure if the function won't work, or maybe something else within the script. I've taken the function out of the $(document).ready() tree, and moved it all around inside of it. I'm sure that isn't the problem now, but I'm starting to not be sure about anything at this point.
I'm trying to get the function to hide the password textbox and submit(or is button better?) and show a textarea for news input, with a button to append the update.The appendedTo and .append() section works, but I can't seem to get the passwordcheck function to work. Sometimes it will alert me if it's wrong, but when it's right the if methods don't seem to work. Then I'll change it a few times and the alert will no longer show, nor will the if work any longer.
Any help would be appreciated, and I can provide any code snippets or chunks at request.
Function in question:
function passwordcheck() {
var $newspass = $('#newspass');
var $submitpass = $('#submitpass'); // <--- variables were at one point selectors
var $newssubmit = $('#newssubmit'); // written out, I've changed this a lot
if ($newspass.val() === 'comecorrecT') {
$submitpass.css('display', 'hidden');
$newspass.css('display', 'hidden');
$('#newsinput').css('display', 'block');
$newssubmit.css('display', 'static');
} else {
alert("Try again, please.");
}
};
Rest of the script, for reference:
$(document).ready(function(){
// billboard functions
var $billboard = $('.billboard');
$billboard.mouseenter(function(){
$(this).fadeTo('slow', 0.98);
});
$billboard.mouseleave(function(){
$(this).fadeTo('slow', 0.72);
});
var $learn = $('#learn-more');
$learn.hover(function(){
$(this).fadeTo('slow', 1);
},
function() {
$(this).fadeTo('slow', 0.6);
});
// news and updates/appendedTo
var $submitpass = $('#submitpass');
var $newssubmit = $('#newssubmit');
$submitpass.click(passwordcheck());
$newssubmit.click(function(){
$('#appendedTo').append('<div class="update">'+$('#newsinput').val()+'</div>');
// passwordcheck();
});
});
I've been working with it for a little while now, and I know you guys will have a profound explanation or two.
The way you are doing it now, you are simply passing "undefined" instead of a function (which would be what the passwordcheck function returns) as you are calling the function instead of passing a reference to it in this line:
$submitpass.click(passwordcheck());
Which should be
$submitpass.click(passwordcheck);
In the last block of your code, after
// news and updates/appendedTo
This being said, don't use client side JavaScript for authentication, the password you are checking against is visible for anyone using the site.
Try this:
function passwordcheck() {
var newspass = $('#newspass').val();
var submitpass = $('#submitpass').val();
var newssubmit = $('#newssubmit').val();
if (newspass == 'comecorrecT') {
$('#submitpass').hide();
$('#newspass').hide();
$('#newsinput').show();
} else {
alert("Try again, please.");
}
}

Dynamically loaded onclick function using in function data

Ok, my coding is a little weak, but I'm learning. I have a code simular to
$.post("test.php", {"data":"data"}, function(grabbed){
$.each(grab, function(i, item){
var place = document.getElementById("div");
var para = createElement("p");
para.innerHTML = item.name;
para.onclick = function(){
document.getElementById(?para?).innerHTML = ?item.price?;
}
place.appendChild(para);
});
}, "json");
My question is how would I use say, item.price in the onclick function. I haven't actually tried yet, but I'm pretty sure that referencing the data retrieved from the request won't work in the dynamically loaded onclick function.
My function works a little differently but the basic principle is on here. Any advise would be appreciated, or perhaps someone can point me in the right direction.
I was thinking of assigning ids to the div or para and using those in the function somehow. I also thought of using cookies to store the data I want, but I can't figure it out. Is there something else I need to know or try.
Your question concerns the concept of closures in JavaScript.
Closures are functions that refer to independent (free) variables. In
other words, the function defined in the closure 'remembers' the
environment in which it was created.
Consider the following code snippet:
var foo = (function(){
var remembered = "yea!";
return function(){
console.log("Do you remember? - " + remembered);
};
})();
foo(); // prints: Do you remember? - yea!
That said, looking at your code:
...
// the function assigned to `onclick` should remember
// the variable `item` in the current context.
para.onclick = function(){
document.getElementById(?para?).innerHTML = ?item.price?;
}
...
That works since you're using JQuery's $.each() for iteration. And here is a demonstration: http://jsfiddle.net/55gjy3fj/
However if you were doing the iteration with a plain for-loop, you would have to wrap the handler inside another closure:
var getHandler = function(para, item){
return function(){
document.getElementById(the_id_here).innerHTML = item.price;
};
};
para.onclick = getHandler(para, item);
...
This fiddle demonstrates the point: http://jsfiddle.net/63gh1gof/
Here's a fiddle that summarises both cases:
Since you're already using jQuery, this could be the perfect opportunity to get introduced to .data(). Use it to save an arbitrary object to the dom element, and then reference it later in the onclick-listener.
var para = $('<p/>').html(item.name).data('item', item);
para.click(function() {
$(this).data('item') // item!!!
});
$('#div').append(para);
Actually, here you could write a more efficient method using jQuery's delegated event handlers.
Just attach the event handler to the parent element:
$('#div').on('click', 'p', function() {
$(this).data('item') // item!!!
});

jQuery Event Handler created in loop

So I have a group of events like this:
$('#slider-1').click(function(event){
switchBanners(1, true);
});
$('#slider-2').click(function(event){
switchBanners(2, true);
});
$('#slider-3').click(function(event){
switchBanners(3, true);
});
$('#slider-4').click(function(event){
switchBanners(4, true);
});
$('#slider-5').click(function(event){
switchBanners(5, true);
});
And I wanted to run them through a loop I am already running something like this:
for(i = 1; i <= totalBanners; i++){
$('#slider-' + i).click(function(event){
switchBanners(i, true);
});
}
In theory that should work, but it doesnt seem to once I load the document... It doesnt respond to any specific div id like it should when clicked... it progresses through each div regardless of which one I click. There are more event listeners I want to dynamically create on the fly but I need these first...
What am I missing?
This is a very common issue people encounter.
JavaScript doesn't have block scope, just function scope. So each function you create in the loop is being created in the same variable environment, and as such they're all referencing the same i variable.
To scope a variable in a new variable environment, you need to invoke a function that has a variable (or function parameter) that references the value you want to retain.
In the code below, we reference it with the function parameter j.
// Invoke generate_handler() during the loop. It will return a function that
// has access to its vars/params.
function generate_handler( j ) {
return function(event) {
switchBanners(j, true);
};
}
for(var i = 1; i <= totalBanners; i++){
$('#slider-' + i).click( generate_handler( i ) );
}
Here we invoked the generate_handler() function, passed in i, and had generate_handler() return a function that references the local variable (named j in the function, though you could name it i as well).
The variable environment of the returned function will exist as long as the function exists, so it will continue to have reference to any variables that existed in the environment when/where it was created.
UPDATE: Added var before i to be sure it is declared properly.
Instead of doing something this .. emm .. reckless, you should attach a single event listener and catch events us they bubble up. Its called "event delegation".
Some links:
http://davidwalsh.name/event-delegate
http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-javascript-event-delegation-in-4-minutes/
http://www.sitepoint.com/javascript-event-delegation-is-easier-than-you-think/
http://lab.distilldesign.com/event-delegation/
Study this. It is a quite important thing to learn about event management in javascript.
[edit: saw this answer get an upvote and recognized it's using old syntax. Here's some updated syntax, using jQuery's "on" event binding method. The same principle applies. You bind to the closest non-destroyed parent, listening for clicks ON the specified selector.]
$(function() {
$('.someAncestor').on('click', '.slider', function(e) {
// code to do stuff on clicking the slider. 'e' passed in is the event
});
});
Note: if your chain of initialization already has an appropriate spot to insert the listener (ie. you already have a document ready or onload function) you don't need to wrap it in this sample's $(function(){}) method. You would just put the $('.someAncestor')... part at that appropriate spot.
Original answer maintained for more thorough explanation and legacy sample code:
I'm with tereško : delegating events is more powerful than doing each click "on demand" as it were. Easiest way to access the whole group of slider elements is to give each a shared class. Let's say, "slider" Then you can delegate a universal event to all ".slider" elements:
$(function() {
$('body').delegate('.slider', 'click', function() {
var sliderSplit = this.id.split('-'); // split the string at the hyphen
switchBanners(parseInt(sliderSplit[1]), true); // after the split, the number is found in index 1
});
});
Liddle Fiddle: http://jsfiddle.net/2KrEk/
I'm delegating to "body" only because I don't know your HTML structure. Ideally you will delegate to the closest parent of all sliders that you know is not going to be destroyed by other DOM manipulations. Often ome sort of wrapper or container div.
It's because i isn't evaluated until the click function is called, by which time the loop has finished running and i is at it's max (or worse overwritten somewhere else in code).
Try this:
for(i = 1; i <= totalBanners; i++){
$('#slider-' + i).click(function(event){
switchBanners($(this).attr('id').replace('slider-', ''), true);
});
}
That way you're getting the number from the id of the element that's actually been clicked.
Use jQuery $.each
$.each(bannersArray, function(index, element) {
index += 1; // start from 0
$('#slider-' + index).click(function(event){
switchBanners(index, true);
});
});
You can study JavaScript Clousure, hope it helps

Javascript: Can I avoid closures?

I am trying to make an website. I am putting addEventListener to more elements in a function called more times:
idImag = 0;
function function1()
{
//do something
function2()
}
function function2()
{
//do something
document.getElementById("holder" + idImag).addEventListener('mouseover',function(){
idImag++;
alert('It works');
}
function3(event)
{
alert(3);
}
function1();
function1();
<div id="holder0">Dog</div>
<div id="holder1">Chicken</div>
<div id="holder2">Cow</div>
But there is a problem: Only the last element gets the event listener... the others do nothing after putting the mouse over it.
Then I've googled a little and found out about closures and how variables are kept even after function returned... I didn't understand everything, but I just want to find out how to put the event listeners in function2. Can you help me?
Probably you noticed: I am a newbie. Sorry if the question is stupid or if it has no sense. If you need more details, I will put my whole code, but it has variables and comments in Romanian, so I am not sure if you will understand it. Sorry for my bad English and thank you in advance.
Why not a for loop?
function function3 (event) {
alert(3);
}
for (var idImag = 0; i< numberOfHolders; i++) {
//do something1
//do something2
document.getElementById("holder" + idImag).addEventListener('mouseover',function3);
}
It looks like you just care about the image id, which is available through the id attribute of the element.
Thus you can do:
document.getElementById("holder" + idImag).addEventListener(
'mouseover',
function(){
var id = this.id;
/* Then strip off "holder" from the front of that string */
}
);
This looks correct, in that it will call document.getElementById("holder0").addEventListener(…), then it will call document.getElementById("holder1").addEventListener(…). Closures aren't your problem there.
You can verify this by, eg, using console.log to log the element that you're adding the event listener to (you'll need Firebug installed, or Chrome's developer console open).
Maybe paste the code to http://jsfiddle.net/ so we can try it?

How to call touch or click event from a function for an element which was generated dynamically

EDIT:
I've made the changes Matthew and Yossi suggested and it still doesn't seem to work. Those changes I've edited in the post below too.
It now works!
I have a question for a particular problem I can't solve. If you know this question has been answered please send me the link as an answer. I'm trying not to use a framework in this case, but can use jQuery if necessary.
I have found answers on how to attach listeners via functions but I need something so as I wouldn't have to refactor all the code I already have. I'm a freelancer and am working on somebody else's code.
What happens is that I want to detect a touch event for a touch device. This code should work for a PC too so I need to detect clicks. There's this DIV which is created programatically to which I need to add the click or touch, depending on the device. Originally the function was called from an onmousedown event like this:
arrDivAnswers[c].onmousedown = onQuestionDown;
And this is the function it calls:
function onQuestionDown(e)
{
if(!itemSelected)
{
if(this.getAttribute('data-isCorrect') == 'true')
setStyleQCorrect(this, true);
else
setStyleQIncorrect(this);
this.querySelector('.answerText').style.color = '#ffffff';
this.querySelector('.isCorrect').style.visibility = 'visible';
}
itemSelected = true;
}
This was working fine. Now I've made this one which would try and select the correct event for a click or touch (I need a function because I have to use this more than once - and the isTouchDevice is working fine. I use that on some other apps so that code is pretty short and has been tested):
function detectEventClickOrTouch(element, functionToCall){
//detectEventClickOrTouch(arrDivAnswers[c], 'onQuestionDown');
if(isTouchDevice()){
element.addEventListener("touchend", functionToCall, false);
} else{
element.addEventListener("click", functionToCall, false);
}
}
The DIV element gets created like this on some loop:
arrDivAnswers[c] = document.createElement('div');
console.log( "Answer object #" + c + " = " + arrDivAnswers[c] );
arrDivAnswers[c].className = 'autosize';
arrDivAnswers[c].style.textAlign = 'left';
arrDivAnswers[c].setAttribute('data-isCorrect',false);
arrDivAnswers[c].setAttribute('data-isSelected',false);
divAnswerContainer.appendChild(arrDivAnswers[c]);
And then the events get attached to it like this (the older method has been commented out):
for(c;c < arrQuestions[index].arrAnswers.length;c++)
{
var curAnswer = arrQuestions[index].arrAnswers[c];
arrDivAnswers[c].onmouseover = function (e){setStyleQHover(e.currentTarget)};
arrDivAnswers[c].onmouseout = function (e){setStyleQUp(e.currentTarget)};
// Detect touch here *************************
detectEventClickOrTouch(arrDivAnswers[c], onQuestionDown);
//arrDivAnswers[c].onmousedown = onQuestionDown;
// Detect touch here *************************
arrDivAnswers[c].style.visibility = 'visible';
arrDivAnswers[c].querySelector('.answerText').innerHTML = curAnswer.strAnswer;
arrDivAnswers[c].setAttribute('data-isCorrect',curAnswer.isCorrect);
if(curAnswer.isCorrect)
{
//arrDivAnswers[c].classList.add("correctAnswer");
arrDivAnswers[c].className = "correctAnswer";
}
else
{
//arrDivAnswers[c].classList.remove("correctAnswer");
arrDivAnswers[c].className = "autosize";
}
arrDivAnswers[c].setAttribute('data-isSelected',false);
setStyleQUp(arrDivAnswers[c]);
itemSelected = false;
}
[...]
The debugger is throwing this error:
Uncaught TypeError: Object [object DOMWindow] has no method 'getAttribute'
I'm sure I'm messing up the "this" because I'm not calling the function properly.
I agree the "this" variable is getting messed up. The problem is that you are attaching an anonymous function as the callback that then calls eval on another method. This seems unnecessary.
Could you just do this:
function detectEventClickOrTouch(element, functionToCall){
//detectEventClickOrTouch(arrDivAnswers[c], 'onQuestionDown');
if(isTouchDevice()){
element.addEventListener("touchend", functionToCall, false);
} else{
element.addEventListener("click", functionToCall, false);
}
}
And then when you attach the event just do:
detectEventClickOrTouch(arrDivAnswers[c], onQuestionDown);
Since you now call the onQuestionDown function indirectly by the eval the this context seen by the onQuestionDown is the global namespace and not the the element which fired the event.
You don't need the eval anyway... you can pass the function it self
detectEventClickOrTouch(arrDivAnswers[c], onQuestionDown);
and:
element.addEventListener("touchend", functionToCall, false);

Categories