Can somebody tell me what I am doing wrong?
window.onload = initForm;
function initForm() {
var allTags = document.getElementsByTagName("*");
for(i=0; i<allTags.length; i++) {
if (allTags[i].className.indexOf("textbox") > -1) {
allTags[i].onFocus = fieldSelect;
allTags[i].onBlur = fieldDeSelect;
}
}
}
function fieldSelect() {
this.style.backgroundImage = "url('inputBackSelected.png')";
}
function fieldDeSelect() {
this.style.backgroundImage = "url('inputBack.png')";
}
I am a beginner at JavaScript so I am not used to debugging code yet.
Thanks
Luke
Your problem lies in attaching your event handlers. You should bind to onfocus and onblur (note the lowercase event name).
As a suggestion, you may want to look at a very simple cross browser addEvent() with a quick line of code added to ensure the proper this pointer:
function addEvent(obj, evType, fn, useCapture){
if (obj.addEventListener){
obj.addEventListener(evType, fn, useCapture);
return true;
} else if (obj.attachEvent){
// fix added by me to handle the `this` issue
var r = obj.attachEvent("on"+evType, function(){
retrun fn.apply(obj, arguments);
});
return r;
} else {
alert("Handler could not be attached");
}
}
And then use the addEvent function instead of allTags[i].onfocus = you will probably have better mileage in the future binding events.
addEvent(allTags[i], 'focus', fieldSelect);
addEvent(allTags[i], 'blur', fieldDeSelect);
jsfiddle demonstration
The problem is that when fieldSelect and fieldDeselect are getting called, this refers to the window object, not to the element that fired the event. You might want to consider using jQuery:
$(document).ready(function() {
$('.textbox').focus(fieldSelect).blur(fieldDeselect);
});
function fieldSelect() {
$(this).css('background-image', 'url("inputBackSelected.png")');
}
function fieldDeselect() {
$(this).css('background-image', 'url("inputBack.png")');
}
jQuery takes care of making sure that when your event handlers are getting called, this refers to the element that fired the event.
Two things, the events should be all lower case (onfocus, onblur) and this doesn't point to the object that triggered the event in IE. Try this:
function fieldSelect(e) {
var event;
if(!e) {
event = window.event;
} else {
event = e;
}
event.target.style.backgroundImage = "url('inputBackSelected.png')";
}
function fieldDeSelect(e) {
var event;
if(!e) {
event = window.event;
} else {
event = e;
}
event.target.style.backgroundImage = "url('inputBack.png')";
}
Standards complient browsers will pass an event object to the event handler. IE uses a global window.event object instead. Either way you can use that object to get the target of the event that triggered the handler.
Another, probably preferable option would be to have your functions set and remove a className instead of directly changing the style. Then put a style called maybe selected in your stylesheet that overrides the background image. That way you keep style info and behavior separate.
Instead of window.onload=initform try window.onload=function(){/the init function/}
Also when refering to a function you should use () even if there are no arguments.
Related
I need to use javascript only for this project. Sorry, no jQuery (I feel ashamed as well).
I am adding an addEventListener to a div. "Problem" is that it applies to all its children, too.
Is there a way to avoid this, and have the listener work only for that div?
Thankd in advance.
my code looks like this:
document.getElementById(myObj.id).addEventListener("mousedown", myObjDown, false);
function myObjDown() {
//do stuff here
}
You can tell which element the event actually fired on by reading event.target in your callback.
var el = ...
el.addEventListener('click', function(event){
if (el !== event.target) return;
// Do your stuff.
}, false);
The other option would be to have handlers bound to the child elements to prevent the event from reaching the parent handler, but that is more work and potentially hides events from things that might actually be listening for them above the parent.
Update
Given your example code, you should be able to do this.
var el = document.getElementById(myObj.id);
el.addEventListener("mousedown", myObjDown, false);
function myObjDown(event) {
if (el !== event.target) return;
//do stuff here
}
Also as a general note, keep in mind that none if this will work on IE < 9 because addEventListener is not supported on those.
You can use the currentTarget Event Property
el.addEventListener('click', function(event) {
if (event.currentTarget !== event.target) {
return;
}
// Do your stuff.
}, false);
More details: https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget
Here's an alternative, which keeps your myObjDown function in line with a typical event handler. (using e.target as reference to the event invoking element)
var CssSelector = "div.className";
var elms = document.querySelectorAll(CssSelector);
for (i = 0; i < elms.length; i++) {
elms[i].addEventListener("mousedown", myObjDown.bind(null, {"target":elms[i]}, false);
}
function myObjDown(e) {
console.log("event: %o - target: %o", e, e.target);
var elm = e.target;
//do stuff here
}
It was suggested that ..
this method could cause memory leaks with versions of some browsers. If anyone experiences this or has any valuable insights. Please comment.
an alternative, in this regard would be
var CssSelector = "div.className";
var elms = document.querySelectorAll(CssSelector);
for (i = 0; i < elms.length; i++) {
elms[i].addEventListener("mousedown", myObjDown.bind(null, elms[i].id}, false);
}
function myObjDown(id) {
console.log("element: %o ", document.getElementById(id));
//do stuff here
}
this work for me:
document.getElementById(myObj.id).addEventListener("mousedown", myObjDown, false);
function myObjDown(e) {
var myTarget= ele.target;
while (myTarget!== this) {
myTarget= myTarget.parentNode; //finding correct tag
}
//do stuff here
}
I have searched for a good solution everywhere, yet I can't find one which does not use jQuery.
Is there a cross-browser, normal way (without weird hacks or easy to break code), to detect a click outside of an element (which may or may not have children)?
Add an event listener to document and use Node.contains() to find whether the target of the event (which is the inner-most clicked element) is inside your specified element. It works even in IE5
const specifiedElement = document.getElementById('a')
// I'm using "click" but it works with any event
document.addEventListener('click', event => {
const isClickInside = specifiedElement.contains(event.target)
if (!isClickInside) {
// The click was OUTSIDE the specifiedElement, do something
}
})
var specifiedElement = document.getElementById('a');
//I'm using "click" but it works with any event
document.addEventListener('click', function(event) {
var isClickInside = specifiedElement.contains(event.target);
if (isClickInside) {
alert('You clicked inside A')
} else {
alert('You clicked outside A')
}
});
div {
margin: auto;
padding: 1em;
max-width: 6em;
background: rgba(0, 0, 0, .2);
text-align: center;
}
Is the click inside A or outside?
<div id="a">A
<div id="b">B
<div id="c">C</div>
</div>
</div>
You need to handle the click event on document level. In the event object, you have a target property, the inner-most DOM element that was clicked. With this you check itself and walk up its parents until the document element, if one of them is your watched element.
See the example on jsFiddle
document.addEventListener("click", function (e) {
var level = 0;
for (var element = e.target; element; element = element.parentNode) {
if (element.id === 'x') {
document.getElementById("out").innerHTML = (level ? "inner " : "") + "x clicked";
return;
}
level++;
}
document.getElementById("out").innerHTML = "not x clicked";
});
As always, this isn't cross-bad-browser compatible because of addEventListener/attachEvent, but it works like this.
A child is clicked, when not event.target, but one of it's parents is the watched element (i'm simply counting level for this). You may also have a boolean var, if the element is found or not, to not return the handler from inside the for clause. My example is limiting to that the handler only finishes, when nothing matches.
Adding cross-browser compatability, I'm usually doing it like this:
var addEvent = function (element, eventName, fn, useCapture) {
if (element.addEventListener) {
element.addEventListener(eventName, fn, useCapture);
}
else if (element.attachEvent) {
element.attachEvent(eventName, function (e) {
fn.apply(element, arguments);
}, useCapture);
}
};
This is cross-browser compatible code for attaching an event listener/handler, inclusive rewriting this in IE, to be the element, as like jQuery does for its event handlers. There are plenty of arguments to have some bits of jQuery in mind ;)
How about this:
jsBin demo
document.onclick = function(event){
var hasParent = false;
for(var node = event.target; node != document.body; node = node.parentNode)
{
if(node.id == 'div1'){
hasParent = true;
break;
}
}
if(hasParent)
alert('inside');
else
alert('outside');
}
you can use composePath() to check if the click happened outside or inside of a target div that may or may not have children:
const targetDiv = document.querySelector('#targetDiv')
document.addEventListener('click', (e) => {
const isClickedInsideDiv = e.composedPath().includes(targetDiv)
if (isClickedInsideDiv) {
console.log('clicked inside of div')
} else {
console.log('clicked outside of div')
}
})
I did a lot of research on it to find a better method. JavaScript method .contains go recursively in DOM to check whether it contains target or not. I used it in one of react project but when react DOM changes on set state, .contains method does not work. SO i came up with this solution
//Basic Html snippet
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="mydiv">
<h2>
click outside this div to test
</h2>
Check click outside
</div>
</body>
</html>
//Implementation in Vanilla javaScript
const node = document.getElementById('mydiv')
//minor css to make div more obvious
node.style.width = '300px'
node.style.height = '100px'
node.style.background = 'red'
let isCursorInside = false
//Attach mouseover event listener and update in variable
node.addEventListener('mouseover', function() {
isCursorInside = true
console.log('cursor inside')
})
/Attach mouseout event listener and update in variable
node.addEventListener('mouseout', function() {
isCursorInside = false
console.log('cursor outside')
})
document.addEventListener('click', function() {
//And if isCursorInside = false it means cursor is outside
if(!isCursorInside) {
alert('Outside div click detected')
}
})
WORKING DEMO jsfiddle
using the js Element.closest() method:
let popup = document.querySelector('.parent-element')
popup.addEventListener('click', (e) => {
if (!e.target.closest('.child-element')) {
// clicked outside
}
});
To hide element by click outside of it I usually apply such simple code:
var bodyTag = document.getElementsByTagName('body');
var element = document.getElementById('element');
function clickedOrNot(e) {
if (e.target !== element) {
// action in the case of click outside
bodyTag[0].removeEventListener('click', clickedOrNot, true);
}
}
bodyTag[0].addEventListener('click', clickedOrNot, true);
Another very simple and quick approach to this problem is to map the array of path into the event object returned by the listener. If the id or class name of your element matches one of those in the array, the click is inside your element.
(This solution can be useful if you don't want to get the element directly (e.g: document.getElementById('...'), for example in a reactjs/nextjs app, in ssr..).
Here is an example:
document.addEventListener('click', e => {
let clickedOutside = true;
e.path.forEach(item => {
if (!clickedOutside)
return;
if (item.className === 'your-element-class')
clickedOutside = false;
});
if (clickedOutside)
// Make an action if it's clicked outside..
});
I hope this answer will help you !
(Let me know if my solution is not a good solution or if you see something to improve.)
I want to apply a click event to an entire page in Javascript, to everything but a single banner on top. Let's say that the banner that I don't want the event in has an id of 'bannerID'. I tried doing the following:
document.onclick = function(){clickEvent()}
document.getElementById("bannerID").onclick = function(){return false;}
However, it looks like the document event overrides everything. Does anyone have any advice?
Check for the element-id within the handler, something like;
document.onclick = clickEvent;
function clickEvent(e) {
var from = e.target || e.srcElement;
if (from.id === 'bannerID') { return; }
/* ... the handling continues ... */
}
This is called event delegation
Pass in an event parameter to the callback and check if the id is 'bannerID'
document.onclick =
function(event){
if(event.target.id != 'bannerID'){
clickEvent()
}
};
demo
In the code you have above, you are adding two click events to "bannerID" - so it will execute clickEvent() and return false.
You can, however, exclude the specific element from the onclick function. This might help:
document.onclick = function(e) {
if (e.target.id === 'bannerID') {
return false;
} else {
clickEvent();
}
};
I need to capture an event instead of letting it bubble. This is what I want:
<body>
<div>
</div>
</body>
From this sample code I have a click event bounded on the div and the body. I want the body event to be called first. How do I go about this?
Use event capturing instead:-
$("body").get(0).addEventListener("click", function(){}, true);
Check the last argument to "addEventListener" by default it is false and is in event bubbling mode. If set to true will work as capturing event.
For cross browser implementation.
var bodyEle = $("body").get(0);
if(bodyEle.addEventListener){
bodyEle.addEventListener("click", function(){}, true);
}else if(bodyEle.attachEvent){
document.attachEvent("onclick", function(){
var event = window.event;
});
}
IE8 and prior by default use event bubbling. So I attached the event on document instead of body, so you need to use event object to get the target object. For IE you need to be very tricky.
I'd do it like this:
$("body").click(function (event) {
// Do body action
var target = $(event.target);
if (target.is($("#myDiv"))) {
// Do div action
}
});
More generally than #pvnarula's answer:
var global_handler = function(name, handler) {
var bodyEle = $("body").get(0);
if(bodyEle.addEventListener) {
bodyEle.addEventListener(name, handler, true);
} else if(bodyEle.attachEvent) {
handler = function(){
var event = window.event;
handler(event)
};
document.attachEvent("on" + name, handler)
}
return handler
}
var global_handler_off = function(name, handler) {
var bodyEle = $("body").get(0);
if(bodyEle.removeEventListener) {
bodyEle.removeEventListener(name, handler, true);
} else if(bodyEle.detachEvent) {
document.detachEvent("on" + name, handler);
}
}
Then to use:
shield_handler = global_handler("click", function(ev) {
console.log("poof")
})
// disable
global_handler_off("click", shield_handler)
shield_handler = null;
I need to temporarily change the click event for an element as follows:
var originalEvent = '';
$("#helpMode").click(function (e) {
originalEvent = $("#element").getCurrentClickEventHandler();
$("#element").click(function (e) {
//Do something else
});
});
//Later in the code
$("#helpModeOff").click(function (e) {
$("#element").click(originalEvent);
});
How would I store the current function that is an event handler in a global variable for later reuse?
EDIT: Here's what im trying to do:
var evnt = '';
$("#helpTool").click(function (e) {
if(!this.isOn){
evnt = $("#Browse").data('events').click;
$("#ele").unbind('click');
$("#ele").click(function (e) {
alert('dd');
});
this.isOn=true;
}else{
this.isOn = false;
alert('off');
$("#ele").unblind('click');
$("#ele").click(evnt);
}
});
Here you go, figured it out:
Now with e.srcElement.id you can get either HelpMode or HelpModeOff and then can turn on/off your help stuff!
http://jsfiddle.net/zcDQ9/1/
var originalEvent = '';
$('#element').on('yourCustomEvent', function (e) {
// do stuff
alert(originalEvent);
$(this).toggleClass('toggleThing');
//test for helpMode or helpModeOff here now...
});
$("#helpMode").on('click', function (e) {
originalEvent = e.srcElement.id;
$("#element").trigger('yourCustomEvent');
});
//Later in the code
$("#helpModeOff").on('click', function (e) {
originalEvent = e.srcElement.id;
$("#element").trigger('yourCustomEvent');
});
Okay. In jQuery 1.7 I guess it's a little different.
//get the handler from data('events')
$.each($("#element").data("events"), function(i, event) {
if (i === "click") {
$.each(event, function(j, h) {
alert(h.handler);
});
}
});
http://jsfiddle.net/yQwZU/
This is the reference.
Not sure if the following works with 1.7.
originalEvent = $('#element').data('events').click;
jQuery stored all the handlers in data. See here to learn more about data('events').
Personally, I think I would avoid manually binding and unbinding handlers.
Another way to approach this is to bind click events to classes, then all you need to do is add and remove classes from the appropriate elements when switching to/from help mode.
Here's a jsfiddle illustrating what I mean.
Switching to and from help mode then just involves adding removing classes:
$('#btnhelpmode').click(function(){
if(!helpMode){
helpMode = true;
$('.normalmode').addClass('helpmode').removeClass('normalmode');
$(this).val('Switch to normal mode...');
}else{
helpMode = false;
$('.helpmode').addClass('normalmode').removeClass('helpmode');
$(this).val('Switch to help mode...');
}
});
and you just create the handlers required, binding them to the appropriate classes:
$('#pagecontent').on('click', '#element1.normalmode', function(){
alert('element1 normal mode');
});
$('#pagecontent').on('click', '#element1.helpmode', function(){
alert('element1 help mode');
});
$('#pagecontent').on('click', '#element2.normalmode', function(){
alert('element2 normal mode');
});
$('#pagecontent').on('click', '#element2.helpmode', function(){
alert('element2 help mode');
});