Trouble in Javascript event handling - javascript

Only today I figured out how event propagation works and set out pompously to test it on my existing code base but arghhhhhhh damn you javascript......nothing seems to be simple enough with you :X
Here is my problem, I define a set of events on an anchor:
theLink.setAttribute('onMouseOver','doSomething(this)'); **// works**
theLink.addEventListener("mouseout", function(event){doSomethingElse(event)}, false); **// does not work**
theLink.onmouseout = function(event){doSomethingElse(event)}; **// does not work**
Only if I define events as in the first example then it seems to be working in the second or the third definitions as well. But I can not use that definition because I have to pass event object.
Any hints? I am using firefox.

All three of these worked for me using the following code (in firefox):
HTML:
<a id="link1">Link 1</a>
<a id="link2">Link 2</a>
<a id="link3">Link 3</a>
JS:
var link1 = document.getElementById("link1");
var link2 = document.getElementById("link2");
var link3 = document.getElementById("link3");
window.doSomething = function(event) {
console.log(event);
}
link1.setAttribute('onMouseOver', 'doSomething(this)');
link2.addEventListener("mouseout", function(event) {
doSomething(event)
}, false);
link3.onmouseout = function(event) {
doSomething(event)
};
Here is a jsfiddle with it working: http://jsfiddle.net/magicaj/qk6wU/
You might also consider using a library like jQuery that handles cross browser incompatibility with the addEventListener method that is not supported by some versions of IE, the JS would look something like this:
$("#link1").mouseover(doSomething);
$("#link2").mouseover(doSomething);
$("#link3").mouseover(doSomething);

An answer with cross browserness included
function doSomething( event ) {
if( console && console.log ) {
console.log( this );
console.log( event );
}
else {
alert( this === window ? 'WINDOW' : this.tagName );
alert( event );
}
}
var link1 = document.getElementById("link1");
var link2 = document.getElementById("link2");
var link3 = document.getElementById("link3");
// `this` within your handler will be `window` and not the link
link1.setAttribute( 'onMouseOver', 'doSomething( event )' );
// to attach event listeners you have to do a bit more work
// ( best to make a function rather than doing this each time
if( document.addEventListener ) {
// compliant browsers
link2.addEventListener('mouseover', doSomething, false);
} else {
// internet explorer
link2.attachEvent( 'onmouseover', function() {
// grab the event object from the window
var e = window.event;
// normalise it a bit i.e. make it a bit more like a compliant browsers event object
e.target = e.srcElement;
e.preventDefault = function(){ e.returnValue = false };
e.stopPropagation = function(){ e.cancelBubble = true };
// and forward to your handler making sure that `this` is properly set
doSomething.call( this, e );
});
}
link3.onclick = doSomething;
Note
Avoid wrapping your handlers in unecessary anonymous functions, it's wastefull and you lose the this in the handler
so instead of
link3.onclick = function( event ) { doSomething( event ) };
just assign the handler directly
link3.onclick = doSomething;

Related

How to run event listener only once? [duplicate]

I'm trying to remove an event listener inside of a listener definition:
canvas.addEventListener('click', function(event) {
click++;
if(click == 50) {
// remove this event listener here!
}
// More code here ...
How could I do that? this = event...
You need to use named functions.
Also, the click variable needs to be outside the handler to increment.
var click_count = 0;
function myClick(event) {
click_count++;
if(click_count == 50) {
// to remove
canvas.removeEventListener('click', myClick);
}
}
// to add
canvas.addEventListener('click', myClick);
EDIT: You could close around the click_counter variable like this:
var myClick = (function( click_count ) {
var handler = function(event) {
click_count++;
if(click_count == 50) {
// to remove
canvas.removeEventListener('click', handler);
}
};
return handler;
})( 0 );
// to add
canvas.addEventListener('click', myClick);
This way you can increment the counter across several elements.
If you don't want that, and want each one to have its own counter, then do this:
var myClick = function( click_count ) {
var handler = function(event) {
click_count++;
if(click_count == 50) {
// to remove
canvas.removeEventListener('click', handler);
}
};
return handler;
};
// to add
canvas.addEventListener('click', myClick( 0 ));
EDIT: I had forgotten to name the handler being returned in the last two versions. Fixed.
canvas.addEventListener('click', function(event) {
click++;
if(click == 50) {
this.removeEventListener('click',arguments.callee,false);
}
Should do it.
You could use a named function expression (in this case the function is named abc), like so:
let click = 0;
canvas.addEventListener('click', function abc(event) {
click++;
if (click >= 50) {
// remove event listener function `abc`
canvas.removeEventListener('click', abc);
}
// More code here ...
}
Quick and dirty working example: http://jsfiddle.net/8qvdmLz5/2/.
More information about named function expressions: http://kangax.github.io/nfe/.
If #Cybernate's solution doesn't work, try breaking the trigger off in to it's own function so you can reference it.
clickHandler = function(event){
if (click++ == 49)
canvas.removeEventListener('click',clickHandler);
}
canvas.addEventListener('click',clickHandler);
element.querySelector('.addDoor').onEvent('click', function (e) { });
element.querySelector('.addDoor').removeListeners();
HTMLElement.prototype.onEvent = function (eventType, callBack, useCapture) {
this.addEventListener(eventType, callBack, useCapture);
if (!this.myListeners) {
this.myListeners = [];
};
this.myListeners.push({ eType: eventType, callBack: callBack });
return this;
};
HTMLElement.prototype.removeListeners = function () {
if (this.myListeners) {
for (var i = 0; i < this.myListeners.length; i++) {
this.removeEventListener(this.myListeners[i].eType, this.myListeners[i].callBack);
};
delete this.myListeners;
};
};
It looks like no one's covered the part of the current JavaScript DOM specification that gives you a mechanism to remove your event listener without using removeEventListener. If we look at https://dom.spec.whatwg.org/#concept-event-listener we see that there are a number of properties that can be passed to control event listening:
{
type (a string)
callback (null or an EventListener object)
capture (a boolean, initially false)
passive (a boolean, initially false)
once (a boolean, initially false)
signal (null or an AbortSignal object)
removed (a boolean for bookkeeping purposes, initially false)
}
Now, there's a lot of useful properties in that list, but for the purposes of removing an event listener it's the signal property that we want to make use of (which was added to the DOM level 3 in late 2020), because it lets us tell the JS engine to remove an event listener by just calling abort() instead of having to bother with removeEventListener:
const canvasListener = (new AbortController()).signal;
canvas.addEventListener('click', () => {
click++;
if (click === 50) {
canvasListener.abort();
} else {
doSomethingWith(click);
}
}, {
signal: canvasListener
});
(Note that this does not use the useCapture flag, because the useCapture flag is essentially completely useless)
And done: the JS engine will abort and clean up our event listener. No keeping a reference to the handling function, no making sure we call removeEventListener with the exact same properties as we called addEventListener: we just cancel the listener.
I think you may need to define the handler function ahead of time, like so:
var myHandler = function(event) {
click++;
if(click == 50) {
this.removeEventListener('click', myHandler);
}
}
canvas.addEventListener('click', myHandler);
This will allow you to remove the handler by name from within itself.
If someone uses jquery, he can do it like this :
var click_count = 0;
$( "canvas" ).bind( "click", function( event ) {
//do whatever you want
click_count++;
if ( click_count == 50 ) {
//remove the event
$( this ).unbind( event );
}
});
Hope that it can help someone.
Note that the answer given by #user113716 work nicely :)
A way to achieve that is use jquery, so you can use:
canvas.click(yourfunction);
then you can detach all event listener with:
canvas.off();
Try this, it worked for me.
<button id="btn">Click</button>
<script>
console.log(btn)
let f;
btn.addEventListener('click', f=function(event) {
console.log('Click')
console.log(f)
this.removeEventListener('click',f)
console.log('Event removed')
})
</script>

Javascript click a class button

So this is just a small personal project that I'm working on using awesomium in .net. So in awesomium I have this browser open and all that and I want to click this button that has this code.
<a class="buttonright" > Bump </a>
But considering it's a class and not a button I'm having trouble finding a way to "click" it. My plan is to use javascript in awesomium to click it but maybe I'm approaching this from the wrong direction?
Thanks
Update:
After a lot of comments (back and forth) I set up a fiddle, with a working version of this code (the code here works, too, but needed some debugging). The eventTrigger function in the fiddle has been stripped of all comments, but I've added an example usage of this function, which is generously sprinkled with comments.
Browse through it, fork it, play around and get familiar with the code and concepts used there. Have fun:
Here's the fiddle
If by "finding a way to click it" you mean: how to programmatically click this anchor element, then this is what you can use:
Here's a X-browser, slightly verbose yet comprehensive approach:
var eventTrigger = function(node, event)
{
var e, eClass,
doc = node.ownerDocument || (node.nodeType === (document.DOCUMENT_NODE || 9) ? node : document);
//after checking John Resig's Pro JavaScript Techniques
//the statement above is best written with an explicit 9
//Given the fact that IE doesn't do document.<NODE_CONSTANT>:
//doc = node.ownerDocument || (node.nodeType === 9 ? node : document);
if (node.dispatchEvent)
{//dispatchEvent method is present, we have an OK browser
if (event === 'click' || event.indexOf('mouse') >= 0)
eClass = 'MouseEvents';//clik, mouseup & mousedown are MouseEvents
else
eClass = 'HTMLEvents';//change, focus, blur... => HTMLEvents
//now create an event object of the corresponding class
e = doc.createEvent(eClass);
//initialize it, if it's a change event, don't let it bubble
//change events don't bubble in IE<9, but most browsers do
//e.initEvent(event, true, true); would be valid, though not standard
e.initEvent(event, !(event === 'change'), true);
//optional, non-standard -> a flag for internal use in your code
e.synthetic = true;//mark event as synthetic
//dispatch event to given node
node.dispatchEvent(e, true);
//return here, to avoid else branch
return true;
}
if (node.fireEvent)
{//old IE's use fireEvent method, its API is simpler, and less powerful
//a standard event, IE events do not contain event-specific details
e = doc.createEventObject();
//same as before: optional, non-standard (but then IE never was :-P)
e.synthetic = true;
//~same as dispatchEvent, but event name preceded by "on"
node.fireEvent('on' + event, e);
return true;//end IE
}
//last-resort fallback -> trigger any directly bound handler manually
//alternatively throw Error!
event = 'on' + event;
//use bracket notation, to use event's value, and invoke
return node[event]();//invoke "onclick"
};
In your case, you can use this function by querying the DOM for that particular element, like so:
var elem = document.querySelector('.buttonright');//IE8 and up, will only select 1 element
//document.querySelectorAll('.buttonright'); returns a nodelist (array-like object) with all elements that have this class
eventTrigger(elem, 'click');
That should have the effect of clicking the anchor element
If you're looking for a way to handle click events on this element (an anchor that has a buttonright class), then a simple event listener is all you need:
document.body.addEventListener('click', function(e)
{
e = e || window.event;
var target = e.target || e.srcElement;
if (target.tagName.toLowerCase() === 'a' && target.className.match(/\bbuttonright\b/))
{//clicked element was a link, with the buttonright class
alert('You clicked a button/link thingy');
}
}, false);
That's the cleanest way to do things (one event listener handles all click events). Of course, you can bind the handler to specific elements, too:
var buttons = document.querySelectorAll('.buttonright'),
handler = function(e)
{
alert('Clicked!');
};
for (var i=0;i<buttons.length;++i)
{
buttons[i].addEventListener('click',handler, false);
}
Depending on how you want to handle the event, there are numerous roads you can take.
The simplest one is this :
<script type="text/javascript">
function buttonRight_onclick(event, sender)
{
alert("HEY YOU CLICKED ME!");
}
</script>
<a class="buttonright" click="buttonRight_onclick(event, this)">
whereas if you were using a framework like jQuery, you could do it like this:
<script type="text/javascript">
$(document).ready(function() {
$(".buttonright").on("click", function(event) {
alert("HEY YOU CLICKED ME!");
});
});
</script>
<a class="buttonright" >Bump</a>
<a class="buttonright" >Also bump</a>
<script type="text/javascript">
function Button_onclick(event, sender)
{
alert("Button Clicked!");
}
</script>
<a class="Button" click="Button_onclick(event, this)">

How do I bind a click to an anchor without a framework (javascript)

I know this is easily done in jQuery or any other framework, but that's not really the point. How do I go about 'properly' binding a click event in pure javascript? I know how to do it inline (I know this is terrible)
click here
and this causes my javascript to execute for a JS enabled browser, and the link to behave normally for those without javascript?
Now, how do I do the same thing in a non-inline manner?
If you need to assign only one click event, you can assign onclick:
If you have an ID:
myAnchor = document.getElementById("Anchor");
myAnchor.onclick = function() { myFunc(); return false; }
you can also walk through all anchors:
anchors = document.getElementsByTagName("a");
for (var i = 0; i < anchors.length; i++) {
anchors[i].onclick = .....
}
There's also a document.getElementsByClassName to simulate jQuery's class selector but it is not supported by all browsers.
If it could be that you need to assign multiple events on one element, go with addEventListener shown by #Jordan and #David Dorward.
The basic way is to use document.getElementById() to find the element and then use addEventListener to listen for the event.
In your HTML:
click here
In your JavaScript:
function myFunc(eventObj) {
// ...
}
var myElement = document.getElementById('some-id');
myElement.addEventListener('click', myFunc);
Or you can use an anonymous function:
document.getElementById('some-id').addEventListener('click', function(eventObj) {
// ...
});
This is a nice cross-browser method
var on = (function(){
if ("addEventListener" in window) {
return function(target, type, listener){
target.addEventListener(type, listener, false);
};
}
else {
return function(object, sEvent, fpNotify){
object.attachEvent("on" + sEvent, function(){
fpNotify(window.event);
});
};
}
}());
on(document.getElementById("myAnchor"), "click", function(){
alert(this.href);
});
The standard go to for this question is on Quirks Mode: http://www.quirksmode.org/js/events_advanced.html
Give it an ID and you should be able to do:
document.getElementById("the id").onclick = function{ ... }
You don't have to use jQuery, but you could try John Resig's popular addEvent funciton.
addevent(elem, "click",clickevent);
function addEvent ( obj, type, fn ) {
if ( obj.attachEvent ) {
obj["e"+type+fn] = fn;
obj[type+fn] = function() { obj["e"+type+fn]( window.event ); }
obj.attachEvent( "on"+type, obj[type+fn] );
} else
obj.addEventListener( type, fn, false );
}
There are more to be considered to'properly' bind an event on HTML tags in pure javascript.
http://www.pagecolumn.com/javascript/bind_event_in_js_object.htm

window.event.srcElement.options NOT Work on FF

window.event.srcElement.options(window.event.srcElement.selectedIndex).value works in Internet Explorer (and Chrome) but not in FireFox. How to make this work in FireFox as well?
event.target.options[event.target.selectedIndex].value. Though as always with events you'd have to have passed the event object into a function, so eg.:
<script>
function selectChanged(event) {
var target= event.target || event.srcElement;
doSomethingWith(target.options[target.selectedIndex].value);
};
</script>
<select onchange="selectChanged(event)">...</select>
Setting the handler directly and using this may be easier:
<select id="x">...</select>
<script>
document.getElementById('x').onchange= function() {
doSomethingWith(this.options[this.selectedIndex].value);
};
</script>
Note that looking at options[selectedIndex] is for compatibility with older browsers. These days you can usually just get away with saying select.value.
There is no global event object in Firefox. Events are passed to their handlers as an argument. Also, instead of srcElement, you look for target.
If you use a javascript library like jQuery, all the browser specific quirks are handled for you.
Otherwise, I suggest you to read these articles
http://www.quirksmode.org/js/introevents.html
http://www.quirksmode.org/js/events_properties.html
var addEvent = (function() {
function addEventIE(el, ev, fn) {
return el.attachEvent('on' + ev, function(e) {
return fn.call(el, e);
});
}
function addEventW3C(el, ev, fn) {
return el.addEventListener(ev, fn, false);
}
return window.addEventListener ? addEventW3C:addEventIE;
})();
var domRef = document.getElementById('foo');
addEvent( domRef, 'change', function(e) {
e = e || window.event;
var el = e.target ? e.target : e.srcElement,
value = el.value;
alert( value )
});
in IE, event is a property of window, in modern DOM supporting browsers it's passed as the first argument.
IE uses srcElement where most other browsers (including Firefox) use target.
Also, Firefox passes around event objects, whereas IE just populates the global event object w/the current event's data.
You'll have to handle both in your code. How you handle the 2nd one will depend on how you're assigning the handler.
But here's one way.
function changeHanlder( event )
{
var elem = event.target || event.srcElement;
alert( elem.options[elem.selectedIndex].value );
}
It's also worth noting that all the modern javascirpt libraries handle this abstraction for you.
There are two approaches:
Assume there is markup
<SELECT name="ddlQuery" id="ddlQuery" style="width:273px;"
onchange="GetDropDownValue(event)">
...
on HTML.
One using js function:
function GetDropDownValue(e)
{
var rtnVal = "";
var sel = document.getElementById(getTargetID(e));
for (var i = 0; i < sel.options.length; ++i) {
if (sel.options[i].selected == true) {
rtnVal = sel.options[i].value;
break;
}
}
alert(rtnVal);
return rtnVal;
}
function getTargetID(e) {
if (!e) { var e = window.event; }
var objTarget = e.srcElement ? e.srcElement : e.target;
return objTarget.id;
}
another using jQuery:
$('#ddlQuery').val()
Firefox uses e.htmlEvent.target.nodeName
you can use try/catch to handle both browsers.

Javascript IE Event

This works in Firefox, but not IE. Any help would be much appreciated!
var form = document.getElementById('theform')
/* create the event handler */
form.gen.onclick = function( evt ) {
var f = evt.target.form
var y = f.year.value
var m = f.month.value
genCalendar( document, y, m, 'theCalendar' )
}
To get the target of an event in both standards compliant browsers and IE, use
var target = evt ? evt.target : window.event.srcElement;
There's an overview of the different properties of event objects at MDC.
As mentioned, IE does not pass the event object as a parameter. Try this:
var form = document.getElementById('theform')
/* create the event handler */
form.gen.onclick = function( evt ) {
if(!evt)
evt = window.event;
var f = evt.target.form
var y = f.year.value
var m = f.month.value
genCalendar( document, y, m, 'theCalendar' )
}
Or better yet, use a cross-browser library, like Prototype.js or jQuery.
When does this script run? You might have to run this script onload, after the DOM is fully loaded
<script>
function go()
{
alert('dom is loaded: register event handlers now') ;
}
</script>
<body onload=" go(); ">
</body>
This is why you should consider using a javascript library such as jquery, YUI, or prototype. These libraries abstract away the browser based differences which simplifies your coding.
From my own searching the most sucessful was this
function clickHandler(e){
var elem, evt = e ? e:event;
if (evt.srcElement) elem = evt.srcElement;
else if (evt.target) elem = evt.target;
alert (''
+'You clicked the following HTML element: \n <'
+elem.tagName.toUpperCase()
+'>'
)
return true;
}
document.onclick=clickHandler;
Sourced from ther very helpful and explanatory http://www.javascripter.net/faq/eventtargetsrcelement.htm

Categories