Help me understand JavaScript events - javascript

I have a table full of cells and i would like to get on which cell the mouse is.
For this i have attached events to all the cells and then i am finding the elements. But i guess there could be a better options. right ?
Is it possible that i attach only single event handler on top and still be able to catch all the information. like which cell user is currently on etc.
Something like below,
<table onMouseOver="monitorFuntion(event)" >...</table>

It's possible to do exactly what you said: You can put a handler on the table, and then find the cell from that. (This is sometimes called "event delegation".) You can do this for some events, including mouseover and mouseout, because they bubble. You can't do it for other events (like blur or focus) because they don't bubble.
Suppose you have a table with the ID "myTable". You can hook up an event handler for mouseover:
var table = document.getElementById("myTable");
if (table.attachEvent) {
table.attachEvent("onmouseover", handleMouseOver);
}
else {
table.addEventListener("mouseover", handleMouseOver);
}
And then handle it like this:
function handleMouseOver(event) {
var target;
// Handle IE event difference from standard
event = event || window.event;
// Find out what element the event actually happened on
// (Another IE difference here, srcElement vs target)
target = event.srcElement || event.target;
// Since that might be an element *within* your cell (like
// a link, or a `span`, or a `strong`, etc.), find the cell
while (target && target.tagName != "TD" && target.tagName != 'BODY') {
target = target.parentNode;
}
if (target && target.tagName != 'BODY') {
// Found one, `target` now points to the cell the mouse is over
}
}
Note that it's important you handle the case where target ends up being null or referring to the body element, because you'll get this event over the table's borders, row padding, etc.
Javascript libraries can help you with this a lot. For instance, the above using Prototype looks like this:
$("myTable").observe("mouseover", handleMouseOver);
function handleMouseOver(event) {
var target;
target = event.findElement("td");
if (target) {
// ...
}
}
jQuery, Closure, and others will similarly help quite a bit.

Based on the code snippet you posted you are looking for event delegation.
Step 1: use jQuery 1.4.2 +
Step 2:
// you can use move, enter, out, over whatever...
$("table").delegate("mouseenter", "td", click, function(){
var tableCell = $(this); // the cell which is currently moused-over.
});

Yes, you can do exactly that, and then use the event object to find the element. The event object differs between IE and other browsers, but getting the "target" is about the same:
function handler(ev) {
ev = ev || window.event;
var targetElement = ('target' in ev) ? ev.target : ev.srcElement;
// ...
}
Now not all events will "bubble up" for you, but I think that the mouse events do. The problems are mostly with "change". Frameworks like jQuery or Prototype generally try to give you more normalized behavior.
edit fixed for IE compatibility

Related

Prevent all javascript events from firing

I am working on a firebug like javascript element selector, but cannot figure out how to stop all JavaScript events from firing when clicked. The firebug lite plugin (https://getfirebug.com/firebuglite) is doing exactly what I want, but cannot figure out what they are doing.
Any help would be appreciated.
Senario:
User selects element inspector
User clicks on element
onClick, mousedown, mouseup should NOT fire
I have tried the following with no luck:
function stopEvents(el){
for(var key in window) {
if (key.indexOf("on") == 0)
el.addEventListener(key.substr(2), stop, false);
}
}
function StopEvent(pE)
{
stopEvents(pE);
if (!pE)
if (window.event)
pE = window.event;
else
return;
if (pE.cancelBubble != null)
pE.cancelBubble = true;
if (pE.stopPropagation)
pE.stopPropagation();
if (pE.preventDefault)
pE.preventDefault();
if (window.event)
pE.returnValue = false;
if (pE.cancel != null)
pE.cancel = true;
}
EDIT:
$('.somediv').on("click", function(e){
//Stop bubbling and propagation
StopEvent(e);
//EDIT: Still not working with this
e.stopImmediatePropagation();
//RUN only my code here
console.log("My code is running still");
return false;
});
If there is another library such as YUI binding events to the same DOM element. It will fire there event after mine. I cannot seem to hijack the event to stop this from happening.
EDIT:
I cannot use disabled because I need to be able to fire my event. If I did the following, I wouldn't be able to fire the above event. I cannot attach a parent event either because the DOM will stop firing all events on the Tree for that node.
$('.somediv').on("mouseover", function(e){
$(this).attr("disabled", "disabled");
});
EDIT:
The events I want to disable are already created before my script runs. These events could be any javascript library such as YUI, Dojo, jQuery, JavaScript etc...
Disabling all events on the page is very easy. Hard part is to restore them when needed.
document.body.innerHTML = document.body.innerHTML;
This will effectively remove all events bound to DOM nodes by replacing the DOM with it's "virgin" copy.
Most of the time user won't even notice the redraw.
You can't "disable" all of them without also intercepting the actual event binding, so you'd have to end up with something like this:
(function(prototypes) {
prototypes.forEach(function(prototype) {
var eventTracker = {};
var oldAEL = prototype.addEventListener;
prototype.addEventListener = function(a,b,c) {
if (!eventTracker[a]) { eventTracker[a] = true; }
return oldAEL.call(this, a, function(evt) {
console.log(a, eventTracker[a]);
if(eventTracker[a] === true) {
b(evt);
}
},c);
};
prototype.toggleEvent = function(name, state) {
eventTracker[name] = state;
};
});
}([Document.prototype, HTMLElement.prototype, ...]));
example: http://jsfiddle.net/BYSdP/1/
the button gets three click listeners, but if the second button is clicked, the event regulator for "click" is set to false, so none of the events will actually trigger the originally supplied code. Note that this also makes debugging a LOT harder, because you're wrapping handlers in anonymous functions.
event.stopImmediatePropagation() keeps the rest of the handlers from being executed and prevents the
event from bubbling up the DOM tree.
Example:
$( "p" ).click(function( event ) {
event.stopImmediatePropagation();
});
$( "p" ).click(function( event ) {
// This function won't be executed
$( this ).css( "background-color", "#f00" );
});
Source: https://api.jquery.com/event.stopimmediatepropagation/

How to detect when a Container loses focus

I have a container (in a form) that has a Table layout with a set of Edit fields (texts, Checkboxes, etc).
I need to capture when the user clicks outside the container (on a menu item for example). There are no event handlers on the container currently.
I know this is an old question, but I recently ran into the same problem so I thought I would try to help any fellow sufferers.
I got stuck for hours creating various incarnations of blur and click event listeners which all seemed to almost work. Blur would fail because it fires even if the focus is on a child element. Click worked but didn't handle keyboard navigation.
My final solution was to capture the focus event at the window level and compare the focus target with my container and it's children. This will only work for browsers that support addEventListener. In my app, I had a captive audience and didn't need to worry about IE < 9.
First create a function to check if the focus target is your container or any of it's child elements.
var LocalTarget = function( el, target )
{
if ( el === target )
{
return true;
}
else if ( el.childNodes )
{
var els = el.childNodes;
for ( var i = 0, n = els.length; i < n; i++ )
{
if ( els[i] === target )
{
return true;
}
else if ( els[i].childNodes )
{
if ( LocalTarget(els[i], target) ) return true;
}
}
}
return false;
};
Note that this will compare all nodes in the container through recursion.
Next, create a listener function.
var Listener = function( e )
{
// Check if receiving element is part of the container.
if ( !LocalTarget([YOUR CONTAINER], e.target) )
{
// Do focus lost stuff here...
// Remove the event listener. [OPTIONAL]
window.removeEventListener( 'focus', Listener, window, true );
}
};
Note that the LocalTarget and Listener functions as well as [YOUR CONTAINER] are closures.
Finally, add the event listener.
window.addEventListener( 'focus', Listener, window, true );
While it seems like a lot of work to go through and the overhead is insane, this is the only concoction I could make work. Hope it helps someone.
Here is a solution using the focusout event, Node.contains method, and the relatedTarget property. This listens for the focusout event on a containing element. When the related target is no longer a descendent of that containing element you know that the focus has gone outside of that element.
let element = document.querySelector('.some-selector');
element.addEventListener('focusout', e => {
if (! element.contains(e.relatedTarget)) {
// Focus has left the element
}
});
This might not work in IE11 since IE11 has only partial support for the Node.contains method.
In jquery you can use $('#container').blur(function() { //some code here });

How can I check on which element the event was triggered?

addLBEvent : function()
{
var amount = this.imageList.length;
for(var i = 0;i < amount;i++)
{
if(this.imageList[i].addEventListener)
{
this.imageList[i].addEventListener("click",this.startLB,false);
}
/*
IE<9-part
*/
}
},
startLB : function(src)
{
}
I'd like to know which element triggered the event.
If I'd do this in the HTML-Code I'd write something like onlick="startLB(this.src)" for example. How can I do such a thing with addEventListener?
I've already tried `addEventListener("click","myobjectname.startLB(this.src)" but it didn't work.
And sorry for my bad English
An event object is passed in as the first argument to any event handler.
The event object as a target property identifying the element to which the event applies.
addLBEvent : function(event) {
console.log(event.target);
}
https://developer.mozilla.org/en/DOM/event.target
You can access a reference to the element that triggered the event...
var elementThatTriggeredEvent = e.target || e.srcElement;
...assuming that e is the reference to the event.
If you want to know which element was clicked, use event.target.
If you want to know which element you had the handler on, use event.currentTarget or, in most cases, this. addEventListener will call your handler with this set to the element on which you called addEventListener.
Note the distinction. For instance, if you had this markup:
<div id="foo"><span id="bar">Hi there</span></div>
...and this code:
document.getElementById("foo").addEventListener('click', function(event) {
alert(this.id);
alert(event.target.id)
}, false);
...then if the user clicks the text "Hi there", this will be the div but event.target will be the span.
Live example | source
See how this is the element you hooked the event on, and event.target is the element on which it fired (and then it bubbled up to the div).
Note that addEventListener isn't available on older versions of IE; you have to use attachEvent instead. attachEvent doesn't ensure that this is set to the element on which you hooked it, so beware that difference between APIs. To smooth things like that out, you might look to any decent library, like jQuery, YUI, Closure, or any of several others.
Use this:
startLB : function(src)
{
var element = src.target.tagName;
alert("element >> "+element);
}
I think it's best for you to bind it like this:
var that = this;
this.imageList[i].addEventListener("click",function() {
that.startLB(this.src);
},false);
this will become the event target, so we have to access the object somehow, I named that that
try this...
addLBEvent : function(e)
{
if (!e) e = event;
e = e.srcElement || e.target;
.......
}

Retrieving previously focused element

I would like to find out, in Javascript, which previous element had focus as opposed to the current focus. I've been looking through the DOM and haven't found what I need, yet. Is there a way to do this any help would be much appreciated
Each time an element is focused, you'd have to store which one it was. Then when another element is focused, you could retrieve the variable for the previous focused element.
So basically, your single focus handler would do 2 things:
Check if previousFocus is defined. If it is, retrieve it.
Set previousFocus to the currently focused element.
Here is a quick demo with jQuery (you can use raw JS too... just fewer lines w jQuery, so it's easier to understand imo):
// create an anonymous function that we call immediately
// this will hold our previous focus variable, so we don't
// clutter the global scope
(function() {
// the variable to hold the previously focused element
var prevFocus;
// our single focus event handler
$("input").focus(function() {
// let's check if the previous focus has already been defined
if (typeof prevFocus !== "undefined") {
// we do something with the previously focused element
$("#prev").html(prevFocus.val());
}
// AFTER we check upon the previously focused element
// we (re)define the previously focused element
// for use in the next focus event
prevFocus = $(this);
});
})();
working jsFiddle
Just found this question while solving the exact same problem and realised it was so old the jQuery world has moved on a bit :)
This should provide a more effective version of Peter Ajtais code, as it will use only a single delegated event handler (not one per input element).
// prime with empty jQuery object
window.prevFocus = $();
// Catch any bubbling focusin events (focus does not bubble)
$(document).on('focusin', ':input', function () {
// Test: Show the previous value/text so we know it works!
$("#prev").html(prevFocus.val() || prevFocus.text());
// Save the previously clicked value for later
window.prevFocus = $(this);
});
JSFiddle: http://jsfiddle.net/TrueBlueAussie/EzPfK/80/
Notes:
Uses $() to create an empty jQuery object (allows it to be used immediately).
As this one uses the jQuery :input selector it works with select & button elements as well as inputs.
It does not need a DOM ready handler as document is always present.
As the previously focused control is required "elsehere" is is simply stored on window for global use, so it does not need an IIFE function wrapper.
Well depending on what else your page is doing, it could be tricky, but for starters you could have a "blur" event handler attached to the <body> element that just stashes the event target.
To me this seems a slight improvement on Gone Coding's answer:
window.currFocus = document;
// Catch focusin
$(window).on( 'focusin', function () {
window.prevFocus = window.currFocus;
console.log( '£ prevFocus set to:');
console.log( window.currFocus );
window.currFocus = document.activeElement;
});
... there's no stipulation in the question that we're talking exclusively about INPUTs here: it says "previous elements". The above code would also include recording focus of things like BUTTONs, or anything capable of getting focus.
document.getElementById('message-text-area').addEventListener('focus',
event => console.log('FOCUS!')
);
event.relatedTarget has all the data about the previously focused element.
See also https://developer.mozilla.org/en-US/docs/Web/API/Event/Comparison_of_Event_Targets
Here is a slightly different approach which watches both focusin and focusout, in this case to prevent focus to a class of inputs:
<input type="text" name="xyz" value="abc" readonly class="nofocus">
<script>
$(function() {
var leaving = $(document);
$(document).on('focusout', function(e) {
leaving = e.target;
});
$( '.nofocus' ).on('focusin', function(e) {
leaving.focus();
});
$( '.nofocus' ).attr('tabIndex', -1);
});
</script>
Setting tabIndex prevents keyboard users from "getting stuck".

Simple click event delegation not working

I have a div
<div class="myDiv">
somelink
<div class="anotherDiv">somediv</div>
</div>
Now, using event delegation and the concept of bubbling I would like to intercept clicks from any of myDiv, myLink and anotherDiv.
According to best practices this could be done by listening for clicks globally (hence the term 'delegation') on the document itself
$(document).click(function(e) {
var $eventElem = $(e.target);
var bStopDefaultClickAction = false;
if ($eventElem.is('.myDiv'))
{
alert('Never alerts when clicking on myLink or anotherDiv, why????');
bStopDefaultClickAction = true;
}
return bStopDefaultClickAction;
});
See my alert question above. I was under the impression that clicks bubble. And it somewhat does because the document actually receives my click and starts delegating. But the bubbling mechanism for clicks on myLink and anotherDiv doesn't seem to work as the if-statement doesn't kick in.
Or is it like this: clicks only bubble one step, from the clicked src element to the assigned delegation object (in this case the document)? If that's the case, then I need to handle the delegation like this:
$('.myDiv').click(function(e) {
//...as before
});
But this kind of defeates the purpose of delegation as I now must have lots of 'myDiv' handlers and possibly others... it's dead easy to just have one 'document' event delegation object.
Anyone knows how this works?
You should use live event from JQuery (since 1.3), it use event delegation :
http://docs.jquery.com/Events/live
So you code will be :
$(".myDiv").live("click", function(){
alert('Alert when clicking on myLink elements. Event delegation powaa !');
});
With that, you have all the benefices of event delegation (faster, one event listener etc..), without the pain ;-)
The event target will not change. You need to mirror what jquery live does and actually check if $eventElem.closest('. myDiv') provides a match.
Try:
$(document).click(function(e) {
var $eventElem = $(e.target);
var bStopDefaultClickAction = false;
if ( $eventElem.closest('.myDiv').length )
{
alert('Never alerts when clicking on myLink or anotherDiv, why????');
bStopDefaultClickAction = true;
}
return bStopDefaultClickAction;
});
Event.target is always the element that triggered the event, so when you click on 'myLink' or 'anotherDiv' you store a reference to these objects using $(e.target); So what you do in effect is: $('.myLink').is('.myDiv') which returns false, and that's why the alert() is not executed.
If you want to use event delegation this way, you should check wheter event.target is the element or any of its children, using jQuery it could be done like this:
$(e.target).is('.myDiv, .myDiv *')
Seems to work fine to me. Try it here: http://jsbin.com/uwari
Check this out: One click handler in one page
var page = document.getElementById("contentWrapper");
page.addEventListener("click", function (e) {
var target, clickTarget, propagationFlag;
target = e.target || e.srcElement;
while (target !== page) {
clickTarget = target.getAttribute("data-clickTarget");
if (clickTarget) {
clickHandler[clickTarget](e);
propagationFlag = target.getAttribute("data-propagationFlag");
}
if (propagationFlag === "true") {
break;
}
target = target.parentNode;
}
});

Categories

Resources