Handling onclick events - javascript

I have a list which contains links . I am using this code to access them:
function initAll() {
var allLinks = document.getElementById("nav").getElementsByTagName("a");
for (var i=0; i< allLinks.length; i++) {
allLinks[i].onmouseover = showPreview;
allLinks[i].onmouseout = function() {
document.getElementById("previewWin").style.visibility = "hidden";
allLinks[i].onclick=mainProcess;
}
}
}
function mainProcess(evt){
alert(this.value);
false;
}
This is not the exact code, what I am trying to do is that I need to identify link is clicked and perform some function on the basis of link clicked. I don't know where code needs to be modified... Page is giving error on the allLinks[i].onclick=mainProcess(this); line.
Now the problem is that I don't know how I should handle all the three events?

1) You're setting the onclick property of each of the links to be the value returned by mainProcess() - which always returns false. So, in effect, you're writing allLinks[i].onclick = false;
2) When you define an event handler directly, the argument that gets passed to it when the event fires, is the event object - not the element it was fired on.
To figure out the element, you can either look in the event object, or (since the handler has been added to the element itself) simply use this, as that will refer to the link element
for (var i = 0; i < allLinks.length; i++) {
allLinks[i].onclick = mainProcess;
}
function mainProcess(event) {
{
alert(this.value);
return false;
}

You do need to pass this to mainProcess(link). As stated in http://www.quirksmode.org/js/events_tradmod.html "No parentheses!" and "this" chapters. Check it out, there's an example there too. Should be everything you need.

Try changing to this:
for (var i = 0; i < allLinks.length; i++) {
allLinks[i].onclick = mainProcess;
}
function mainProcess(event) {
{
alert(this.value);
return false;
}

Related

Can't detach javascript event after it fires

I'm using this structure to create one-time click events:
function structure() {
this.elements = document.getElementsByClassName('className');
this.numElements = this.elements.length;
for(var i = 0; i < this.numElements; i++) {
this.elements[i].addEventListener('click', this.elementClicked.bind(this));
}
}
The handler of those events is implemented as follows:
structure.prototype.elementClicked = function(e) {
// ... processing event
for(var i = 0; i < this.numElements; i++) {
this.elements[i].removeEventListener('click', arguments.callee);
}
};
The idea is to fire the handler once if any of the registered elements gets clicked, and then unregister the event from each of those elements
Unfortunately the handler still gets fired everytime I click on one of the registered items
I'm aware anonymous functions can't be used to reference the same object, but specifying arguments.callee or the entire name of the referenced function still didn't help the cause
An alternative is to make your objects implement the EventListener interface. You can do this by adding a handleEvent method to the .prototype of the constructor, and then passing the object itself in place of the event handler.
function Structure() {
this.elements = document.getElementsByClassName('className');
this.numElements = this.elements.length;
for(var i = 0; i < this.numElements; i++) { // v-- pass the object
this.elements[i].addEventListener('click', this);
}
}
// Implement the interface; gets invoked when an event occurs
Structure.prototype.handleEvent = function(e) {
// Used a switch statement in anticipation of other event types
switch (e.type) {
case "click":
this.elementClicked(e);
break;
}
};
Structure.prototype.elementClicked = function(e) {
// ... processing event
for(var i = 0; i < this.numElements; i++) { // v-- pass the object
this.elements[i].removeEventListener('click', this);
}
};
Now there's no longer any need to use .bind(). Instead the value of this in handleEvent will be the bound object. You can still get the element to which the handler was bound via e.currentTarget.
Each time you call...
this.elements[i].addEventListener('click', this.elementClicked.bind(this));
... bind creates another instance of a method. It uses this.elementClicked, true, but otherwise is a completely different function. That's why you won't drop it with remoteEventListener called on this.elementClicked.
What's the workarounds? One possible option - passing { once: true } as addEventListener param - has been given in the comments, but it's not supported by IE and Edge (and most likely won't be supported by the Safari you encounter in the nearest future). Here's another approach:
function Structure() {
this.elements = document.getElementsByClassName('className');
this.numElements = this.elements.length;
// reassign a bound method onto instance:
this.elementClicked = this.elementClicked.bind(this);
for(var i = 0; i < this.numElements; i++) {
this.elements[i].addEventListener('click', this.elementClicked);
}
}
Structure.prototype.elementClicked = function(e) {
// ... processing event
for(var i = 0; i < this.numElements; i++) {
this.elements[i].removeEventListener('click', this.elementClicked);
}
};
Now you create a bound elementClicked method for each instance of structure object, having its context set permanently.

Do I need a parameter in this function?

In this homework assignment, I'm having issues with this part of the problem.
window.onload=setup;
function setup()
{
var questions = document.querySelectorAll('ol li');
for (var i= 0; i < questions.length ; i++)
{
questions[i].id = i + "phrases";
questions[i].onmousedown = showEnglish;
//questions[i].onmouseup = showFrench;
questions[i].style.cursor = "pointer";
}
}
function showEnglish()
{
var phraseNumber = parseInt(question[i].id)
document.getElementById(phraseNumber).innerHTML = english[phraseNumber];
english[phraseNumber].style.font = "italic";
english[phraseNumber].style.Color = "rgb(191,22,31)";
}
a) Using the id property of the list item experiencing the mousedown event, extract the index number with the the parseInt() function and store that value in the phraseNumber variable.
I get an error, saying questions is not defined in the showenglish().
Am I supposed to be referencing another object?
You need to pass the question as a parameter:
for(i=0;i<question.length;i++){
let a=i;//important for scoping
question[a].onmousedown=function(){
showEnglish(question[a]);
}
}
function showEnglish(question){
document.getElementById(question.id).style.font="italic";
...
}
(Note: this answer contains ES6. Do not use it in real productional environment. The let a=i; defines that a is kept for being used inside of the listener, while i will always be question.length, because the event is probably clicked after the loop occured...)
Alternatively, the event listener binds this as the clicked element:
question[i].addEventListener("click",showEnglish,false);
function showEnglish(){
document.getElementById(this.id).style.font="italic";
...
}
The mousedown event is raised when the user presses the mouse button. Look at the documentation for the mousedown event.
Your event handler function will be passed an Event object, which has a target property, which is a reference to the element that the mouse clicked on.
You can access this inside your event handler function with event.target.
window.onload = setup;
function setup() {
var questions = document.querySelectorAll('ol li');
for (var i = 0; i < questions.length; i++) {
questions[i].id = i + "phrases";
questions[i].onmousedown = showEnglish;
//questions[i].onmouseup = showFrench;
questions[i].style.cursor = "pointer";
}
}
function showEnglish(event) {
var phraseNumber = parseInt(event.target.id);
// etc
};

keep js function inside jquery object

I have a menu class loading data from a received json file.
in the constructor I build the menu, so I have a for loop with this (extracted part) js:
for (var i = 0; i<data.length; i++)
{
var btn = $('<div>'+data[i].label+'</div>').appendTo(object.container);
btn.click(function()
{
if($('.blockingFrame').length == 0)//pas de blocking
{
data[i].action()
}
});
}
Now, obviously this doesn't work because on runtime data[i] doesn't exist anymore...
data[i].action contains a valid js function.
This works, but doesn't contains the condition..:
for (var i = 0; i<data.length; i++)
{
var btn = $('<div>'+data[i].label+'</div>').appendTo(object.container);
btn.click(data[i].action);
}
So I thought I could store this action inside the jquery object and call it like this, but it doesn't work:
for (var i = 0; i<data.length; i++)
{
var btn = $('<div>'+data[i].label+'</div>').appendTo(object.container);
btn.action = data[i].action;
btn.click(function()
{
if($('.blockingFrame').length == 0)//pas de blocking
{
$(this).action();
}
});
}
A partial solution I came u with, was to store the action in another event, like dblclick, and trigger a dblclick inside the condition, but this seams ugly.
Any idea how to do this?
for loops don't work properly with closures. Consider using iterator methods instead:
$.each(data, function(index, elem) {
var btn = $('<div>'+elem.label+'</div>').appendTo(object.container);
btn.click(function()
{
if($('.blockingFrame').length == 0)//pas de blocking
{
elem.action()
}
});
}
Iterators are usually more elegant and compact than for loops, especially when you have them nested.
The reason why your last snippet doesn't work if that btn = $(...) is a temporary jquery object and disappears once you leave the scope, with everything you have assigned to it. When later a click handler is being invoked, you create a new jquery object via $(this), which doesn't carry your changes from the previous step. If you want to keep any data attached permanently to an element, use the data method - but in this case there's no need for that.
Use an immediately-executing function to create a closure that holds i.
for (var i = 0; i<data.length; i++) {
var btn = $('<div>'+data[i].label+'</div>').appendTo(object.container);
btn.click(function(i) {
return function() {
if($('.blockingFrame').length == 0)//pas de blocking {
data[i].action();
}
}(i));
}

How to get onclick for an HTML object?

Using vanilla JS I would like to know if would be possible to see the property onclick on a HTML object (div)
for (var name in element) {
if(name == "onclick"){
// do smt
}
}
Instead of enumerating properties of element, you can immediately retrieve the onclick property of element with:
var clickHandler = element.onclick;
Events nowadays are bound with addEventListener (and attachEvent in old IE), which allow for multiple handlers per event type. Setting the onevent property only allows for one handler, can be overwritten, and normally isn't the way to bind handlers in a web page.
Unfortunately, you are not able to retrieve any listeners bound with addEventListener (and attachEvent), without writing a wrapper function that tracks them...for example:
var events = [];
function addEvent(element, eventName, callback) {
element.addEventListener(eventName, callback, false);
var found = false;
for (var i = 0; i < events.length; i++) {
if (events[i].el === element) {
found = true;
events[i].list.push(callback);
break;
}
}
if (!found) {
events.push({
el: element,
list: [callback]
});
}
}
function viewEvents(element) {
for (var i = 0; i < events.length; i++) {
if (events[i].el === element) {
return events[i].list;
}
}
return null;
}
And you'd use it like:
var div = document.getElementById("some_id");
addEvent(div, "click", function () {
console.log("whatever");
});
console.log(viewEvents(div));
(of course, you'd need a wrapper for removeEventListener that removes handlers from events too)

Can you attach a handler function with arguments to an onclick event for a number of elements in a loop?

I am trying to set an onclick event for thumbnails that are dynamically populated from a database. I need to set the function to handle an argument, which is the id of the bigger picture the thumbnail represents. The code I have now sets all the thumbnails to point to #18. If you see in the for-loop, it is supposed to die at 17:
for (var i = 0; i < 18; i++) {
document.getElementById('tat' + i).onclick = function() { display(i); };
}
(My thumbnail <img />s all have id="tat0", id="tat1", id="tat2", id="tat3" etc.)
(display() loads the larger pic that the thumbnail represents into a separate element)
Each thumbnail gets this onclick function, so I know the for loop is accessing each one by its ID properly (stepping through for each i) so why are all the display(i) being assigned to 18? Can you assign an onclick function to handle parameters?
You need a closure function to generate your handlers.
function genHandler( param ) {
return function() {
// use all params in here
display( param );
}
}
and then assign your events similarly
for (var i = 0; i < 18; i++) {
document.getElementById('tat' + i).onclick = genHandler( i );
}
It might also work, if you just add 'i' as a parameter to your function.
Wrapping your onclick handler in a function will create a closure that carrys the current scope with it.
for (var i = 0; i < 18; i++) {
document.getElementById('tat' + i).onclick = (function(a) {
return (function() {
display(a);
});
})(i);
}​

Categories