right now I'm trying to learn JavaScript with the book "Beginning JavaScript 5th Edition" and because english is not my native language it's sometimes hard to understand.
Right now I'm a bit confused with the so called DOM standard Event object.
function handleEvent(e) {
var target = e.target;
var type = e.type;
if (target.tagName == "P") {
if (type == "mouseover") {
target.className = "underline"; } else if (type == "mouseout") {
target.className = ""; }
}
};
Is e just a convention for a parameter that I could give any name I want ? For example y ?
Can I think of .target as the same as .this ?
.target references to my element on which an event occurs, right ?
e is just a convention for the parameter, and so is event. Because e has all the event propertys. Try outputting e in your console, and you'll see all the values it has. (Btw, e is just an object as you'll see in your console.)
In your handleEvent function, just add console.log(e) to see everything it has.
e.target is the element which the event is called on. And e.type is the event type. If you switch e to event things might become clearer to you.
e is indeed just a convention, so you could give it any name you want. .target is de element that fires the event, so its the element where you attached the event to.
Yes. e is the standard variable name given to the event object. That variable name could be anything.
Related
NB I've done a bit of reading about JS engine optimisation, but most of it is too technical for me to understand enough to apply to this question. I'm also aware that not all engines are the same. I'd be interested in particular in handling by V8 and Rhino, I suppose.
If I create a table, and then rows, and then cells... And then I want to put identical key event listeners on all those cells.
Not only does the creation of these listeners for each cell take a certain amount of time, which could be significant with a biggish table, but in addition I'm supposing that each listener function is stored on its own, even though every listener function is actually identical.
The other key event listener approach which I can use is to put a key event listener on the TABLE, and to work out during the run, on each keydown event, which cell fired this event. I can do this by going
let elementOfInterest = document.activeElement;
"Get the currently focused element in the document" from here.
From my experiments, if you type inside a table cell, this TD does indeed have the focus and is indeed returned by the above call.
This way, I only have to create one listener, which will I assume be quicker and take less memory. The only (very) slight downside is that time then has to be spent getting this "active element" by means of the above call. And, just possibly, the risk that something will grab focus in an unexpected way - obviously if you want to listen to changes of text in a cell, the least error-prone technique must be to use a listener attached to that cell.
But I'm just wondering: maybe Javascript is cleverer than this: maybe if you create 100 separate cell listeners something somewhere identifies them as "all the same" and just makes one function in memory. This is the kind of optimisation you might typically expect from a Java compiler, for example.
Does any such optimisation ever occur? How clever is Javascript with a case like this? Or is it just "script and that's it": what you see is what you get?
The semantics of the language itself don't allow for two function expressions to be "merged" into one even if they were functionally equivalent:
> a = function(){return 'foo'};
ƒ (){return 'foo'}
> b = function(){return 'foo'};
ƒ (){return 'foo'}
> a === b
false
In addition, things get extra hairy when you start considering the closure of the function (e.g. the outer names it uses).
So no, that doesn't happen out of the box.
However, for your use case, there are two optimizations:
As you've found out, you can employ event bubbling and add the event listener on an ancestor element and use event.target (preferably instead of document.activeElement) to figure out where it was originally targeted (and event.currentTarget would be the node the handler is on)
If you can't use a common ancestor (tip: you almost always can; document is a valid target), you could define the function once (assuming it doesn't need to close over any dynamically changing variables) and again use event.target, e.g. event.target.dataset to figure out the data you're handling.
Below, a snippet demonstrating the two.
function createButton(parent, datum) {
const btn = document.createElement("button");
btn.dataset.datum = datum;
btn.innerHTML = datum;
parent.appendChild(btn);
return btn;
}
function eventHandler(event) {
if(event.target.tagName !== "BUTTON") return;
const msg = `real target: ${event.target} (datum="${event.target.dataset.datum}")\ncurrent target: ${event.currentTarget}`;
alert(msg);
}
const p2 = document.getElementById("parent2");
// bubbling listener
const p1 = document.getElementById("parent1");
p1.addEventListener("click", eventHandler, false);
for(var i = 0; i < 10; i++) {
createButton(p1, "p1-" + i);
}
// same function on multiple elements
for(var i = 0; i < 10; i++) {
createButton(p2, "p2-" + i).addEventListener("click", eventHandler, false);
}
<div id="parent1"></div>
<div id="parent2"></div>
I'm not sure I understand the behavior of IE in this script I'm working. This is part of the script I'm using that seems to work fine in Chrome
$(document).keydown(function (event) {
var keyvalue = event.which || event.keyCode;
var eventtarget = event.srcElement.nodeName || event.target.nodeName;
var readonlycheck = event.srcElement.readOnly || event.target.readOnly;
});
The problem comes in on the readonlycheck variable. In IE I get the error of
"Unable to get property 'readOnly' of undefined or null reference."
In Chrome, readOnly returns 'true' if it's defined and 'false' if it's not. IE gives me an error, even though it still works with the nodeName.
Where I get really confused is that I can make it work by changing the last line to eliminate the target.readOnly. So this seems to work in both browsers...
var readonlycheck = event.srcElement.readOnly;
Can anyone explain why this behaves differently for readOnly? Also, I thought srcElement was IE only, so why is Chrome still working without the target.readOnly?
Any help would be appreciated. I'm still very new to javascript and jquery so I'm sure I'm missing something.
var readonlycheck = event.srcElement.readOnly || event.target.readOnly;
should change it to
var readonlycheck = (event.srcElement !== undefined) ? event.srcElement.readOnly : event.target.readOnly;
how you wrote your code, even though srcElement.readOnly does exist, when it evaluates to false it attempts to read event.target, which breaks IE.
In your code you specify function(e) though in your function you use event instead of e.
Additionally as event.target and event.srcElement are the same thing it would make more sense to initiate the target once at the start.
var target = event.target || event.srcElement;
Updating your code to something similar to the below should work:
$(document).keydown(function (event) {
var target = event.target || event.srcElement;
var keyvalue = event.which || event.keyCode;
var eventtarget = target.nodeName;
var readonlycheck = target.readOnly;
});
The problem is the short-circuit || operator. If event.srcElement.readOnly evaluates to false, the right operand, (which should be) e.target.readOnly will be evaluated. It's the same as writing, for example:
var readonlycheck = false || e.target.readOnly;
You can use braces instead to work around this issue:
var readonlycheck = (e.target || event.srcElement).readOnly
Note that I moved the standards compliant to the left hand side so that it's evaluated first. It doesn't make much of a difference here, but on more time-consuming operations it could, so I just find that it's a good practice to get into.
I start most of my event handlers with the following code when I need the event object and the event target:
function evtHandler(evt) {
evt = evt || window.event;
var target = evt.target || evt.srcElement;
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Javascript event handler on body but not on input
I wrote a short script that listens the keydown event, but i'd like to ignore it if i'm writing in a text field.
I have no idea about how to do it without strange tricks, like checking if there is a focus on one of the inputs.
Here is my code right now.
document.addEventListener('keydown', function(event) {
if(event.keyCode == 71) {
showSelected();
}
else if(event.keyCode == 13) {
closeModal();
}
});
This is a little tricky if you're doing things with contenteditable (like letting users write text inside of any divs/spans/boxes they want to on the page).
If so, this will take a little thinking and reworking (it's not that hard - just more involved).
However, you can check the event.target (element the event's happening on) for its tagName property, against a list of types you want to exclude.
function keyEvent (evt) {
var key = evt.keyCode,
el = evt.target,
type = el.tagName.toLowerCase();
// tag names are upper-case... almost always...
// so convert one way or the other, to be sure
if (type === "input" || type === "textarea") { return; }
/* do whatever you were going to do */
}
Should also likely be noted that this solution (and your code thus-far) aren't ghetto-IE compatible.
For that, you'd need to work with attachEvent and the window.event and window.event.srcElement properties, because they don't support addEventListener and the e/event function-parameter.
just check for the event.target
{
if (event.target.tagName.toUpperCase() == 'INPUT') return false;
// Do your Coding
}
This program taken from a book Pro JavaScript Techniques is used to create hover-like functionality for an Element.
I don`t understand what the author means when he says, in the comments, Normalize the Event object.
Can you tell me
a) why is this necessary, explaining what would happen if it wasn`t normalized
b) how does the code provide achieve the effect
Thank you.
var div = document.getElementsByTagName('div')[0];
div.onmouseover = div.onmouseout = function(e) {
//Normalize the Event object
e = e || window.event;
//Toggle the background colover of the <div>
this.style.background = (e.type == 'mouseover') ? '#EEE' : '#FFF';
};
It's referring to window.event, IE's non-standard version of the event object. If it weren't normalized, it would break in at least one browser.
What the code does is set e to itself (essentially a no-op), if the parameter is truthy (the event parameter is properly set). If not (in IE), it sets it to window.event.
As you know, most of rich text editor use iframe to create WYSIWYG editor. In iframe contain document that is in design mode. I want to capture key down event when user press '#' character in rich text editor for displaying autocomplete for it.
By the way, i cannot see any fired event inside design mode. How can I solve this question?
It's perfectly possible to capture all key events in documents with designMode turned on, though you have to use addEventListener on document in Firefox (and possibly others) rather than assigning your handler to document.onkeypress.
To capture the user typing the '#' character (or indeed any printable character), you should use the keypress event rather than keydown.
// Assuming you have a reference to your iframe in a variable called 'iframe':
function handleIframeKeyPress(evt) {
evt = evt || iframe.contentWindow.event;
var charCode = evt.keyCode || evt.which;
var charTyped = String.fromCharCode(charCode);
if (charTyped === "#") {
alert("# typed");
}
}
var doc = iframe.contentWindow.document;
if (doc.addEventListener) {
doc.addEventListener("keypress", handleIframeKeyPress, false);
} else if (doc.attachEvent) {
doc.attachEvent("onkeypress", handleIframeKeyPress);
} else {
doc.onkeypress = handleIframeKeyPress;
}