This is original code:
h1tag= document.getElementById("myHeading");
h1tag.addEventListener("mouseover", ()=>{h1tag.style.backgroundColor="blue"});
After combining:
h1tag= document.getElementById("myHeading").addEventListener("mouseover", ()=>{h1tag.style.backgroundColor="blue"});
What is the reason behind this?
The problem with this line :
h1tag= document.getElementById("myHeading").addEventListener("mouseover", ()=>{h1tag.style.backgroundColor="blue"});
Is that the statement is evaluated from left to right, so the last method that will be called is addEventListener, so in other words you are trying to store the returned result from addEventListener in your h1tag variable while addEventListener doesn't have a return type so it will return undefined.
To expand on the previous answers, the key issue here is that h1tag is not defined (because addEventListener doesn't return a value) and you are trying to change it's properties.
Fortunately, javascript provides a way to access the element that an event is called on, through an argument passed to the event function.
Try running
document.getElementsById("myHeading")
.addEventListener("mouseover", (e)=>{e.target.style.backgroundColor = "blue"})
h1tag is not defined in your second attempt.
document.getElementById("myHeading").addEventListener("mouseover", (e)=>{e.currentTarget.style.backgroundColor="blue"});
if you still want to kep a reference to the element:
(h1tag=document.getElementById("myHeading")).addEventListener("mouseover", (e)=>{e.currentTarget.style.backgroundColor="blue"});
Related
We can do this,
var someDiv1 = document.createElement('div');
someDiv1.setAttribute('class', 'someClass');
Because of the above code, we naturally tempt to think that below code may work also.
var someDiv2 = document.createElement('div').setAttribute('class', 'someClass');
Actually it works. But the problem is when we try to do this
document.querySelector(".container").appendChild(someDiv2);
It gives an error!!!
Uncaught TypeError: Failed to execute 'appendChild' on 'Node':
parameter 1 is not of type 'Node'.
at HTMLImageElement.
Even though the second code block is working it is useless. Because we can't show the created "div" element.
What is the reason?
The term you're looking for is method chaining.
Each method returns an object, allowing the calls to be chained together in a single statement without requiring variables to store the intermediate results.
Which is NOT the case here.
For this to effectively work, each of the function should return an instance of the created object, which is not the case. .createElement() will return an HTMLDivElement, then you call .setAttribute on the object return by createElement but .setAttribute doesn't return anything. What is assigned to someDiv2 is what is returned by .setAttribute, so nothing. If it would have returned the HTMLDivElement, you could have done a one-liner.
The first code block of the question gives a "div" element as the output. If you run below code
console.log(someDiv1);
you can see the output of someDiv1 variable like this.
<div class="test"></div>
If you execute the above code before the second line of the first code block of the question you will get
<div></div>
as the output. Because it hasn't added the class attribute to the created "div" element yet. And by executing the second line it will add the class attribute to that "div" element.
But if you do the same thing to the someDiv2 variable the output will be undefined. That means the second code block of the question has no output. That's why it gives an error when trying to use appendChild(). But Why ????
The reason is the latter part of the second code block.
.setAttribute('class', 'someClass');
It sets the class attribute to the created "div" element. But doesn't output anything instead it just does something. Even though the former part of the second code block gives an output, the latter part is the one which gives the final output for being assigned to the variable. Since it doesn't give any output there is no value for someDiv2 variable.
But actually that second code block is executed without any problem. The only problem is the variable has no value. So it wouldn't give any errors until we are going to use the someDiv2 variable in somewhere. But the problem is to show the created "div" element we definitely have to use the someDiv2 variable.
So concatenating "setAttribute()" and "createElement()" is useless.
So what if do this instead of assigning to a variable.
document.querySelector(".container").appendChild(document.createElement('div').setAttribute('class', 'someClass');
NO. This would not work as well. You can understand it, since the concatenation of setAttribute() and createElement() doesn't give exact output, appendChild() function has no input too. So it gives errors.
This is same for other functions like setAttribute() and createElement(). Like if you try to concatenate,
getElementsByClassName() and setAttribute()
createElement() and classList.add()
createElement() and className
getElementById() and classList.add()
getElementsByClassName() and className()
those will definitely be executed without any problem but no exact output. Actually those are some examples. There are many combinations like that.
Yes. we usually use that kind of concatenations exactly. But we have to bear in mind that it these kind of concatenations doesn't give any output. Specially if we use "createElement()" it will matter a lot. Because we have to show the output in somewhere.
I am trying to "link" a tag value to a javascript variable (and ideally to a function, but well a variable is a good start), by reference.
I know I can do a myDom.value = myVar but then if the value of myVar change the tag will not be modified.
Is it possible to do this (not using events, because it would be so heavy)?
Thank you :)
No.
You can fake references by assigning objects to things, but only if the property you assign the object to is expecting an object and knows to pull data out of it instead of stringifying it and using it directly.
You could use a closure function around the variable. So instead of attaching the value, you attach a function which will return the variable:
myDom.valueAccessor = function(){ return value }
After this you can access the result as:
myDom.valueAccessor()
This will track the value in value as it changes. Obviously you could choose a snappier name for your function. This seems to work for me in Firefox.
I am using .on() to add listeners a few items in my DOM - one input range field, and a number of blocks with the class .colorBlock. These event listeners only need to be active intermittently, and I would like to turn them .off() when they are not in use. Doing this means using a named function instead of an anonymous one.
So fair enough, except that I need to pass data into the callback functions. I know I can use the second (third?) argument field to pass in an object to the event, which is readable in the callback, but doing so seems to be scoping this to the event, instead of to the DOM node that .on() was listened on. See below for example:
$('#brushSize').on('touchend', { size: $(this).val() }, utils.setBrushSize);
$('.colorBlock').on('touchstart', { color: $(this).data('color') }, utils.setColor);
In my callback functions, I added an alert for e.data.color and e.data.size, and both call out undefined.
To make matters worse, this is a phone gap app, so I am limited in my options to trace what is getting passed around, so some of what I am assuming could be wrong about what is going on.
Any suggestions?
Thanks.
Let's break down this line:
$('#brushSize').on('touchend', { size: $(this).val() }, utils.setBrushSize);
It's exactly the same (other than the variables) as this:
var sizeValue = $(this).val();
$('#brushSize').on('touchend', { size: sizeValue }, utils.setBrushSize);
E.g., you're calling $(this).val(), and then passing the result of calling it in as part of your data object. So unless this is already what you want to get the value from at that point, it's not going to work.
If you want to get some information from the element when the event happens, just put that code in your event handler. For example, looking at this line:
$('.colorBlock').on('touchstart', { color: $(this).data('color') }, utils.setColor);
It looks to me like you're trying to get the color from the .colorBlock element that was touched. If so:
$('.colorBlock').on('touchstart', function() {
utils.setColor($(this).data('color'));
});
Or if you're going to reuse it:
utils.setColorFromEventElement = function() {
utils.setColor($(this).data('color'));
};
and
$('.colorBlock').on('touchstart', utils.setColorFromEventElement);
Side note:
There's also a possible second problem with that line. You're using utils.setBrushSize as the event handler. Note that within the call to setBrushSize, this will refer to the DOM element on which you hooked the event, not utils. Now, given the name utils, maybe that doesn't matter, but I thought I'd mention it.
More: Mythical methods, You must remember this
The value you're sending in the arguments object is always going to be the number it was when you called the .on() statement. That function's not going to be dynamically re-called every time the event fires.
Personally I think it's really ugly to have the util class go looking for some DOM element and get its value, when as you alluded, what you really want to do is have your util function run in the same scope as the .on() statement.
Your first instinct was probably correct. You don't want an anonymous function, because you want to be able to call off(). Ideally you want a named function that runs in the same scope as the thing that calls the on() statement. So what you want to do is bind the util function to your current scope:
$('#brushSize').on('touchend', utils.setBrushSize.bind(this));
Then in utils.setBrushSize, $(this) is whatever function you called .on() from.
edit Just a warning on this though: when you call off(), you want to call it like this:
$('#brushSize').off('touchend', utils.setBrushSize);
Not on a new scope-bound version of setBrushSize. JQuery should recognize it as equal to the original function you bound and turn it off.
re-edit I'm realizing now that your val() is in $('#brushSize') as that's the "this" you're trying to call... not the function holding the on statement. In that case you can do it this way:
$('#brushSize').on('touchend', utils.setBrushSize.bind($(this)));
So the solution for this particular problem ended up requiring that I strip this bit of code out of Phone Gap and rebuild it in a browser. I was then able to console.log the event that was being sent to the callbacks, and examine them to understand the event object better.
The solutions was to use event.target. This allowed to get the event.target.dataset.color for the .colorBlock listener, and event.target.value from the brushSize range listener.
So for future me, I would be good to have a solid working version of my app in the browser with the phone gap stuff stripped out, to do better testing for problems like this.
I am creating a few DOM elements dynamically like,
var anchorElement = jQuery('<a />',{text:property.text});
var liElement = jQuery('<li />',{"class":"navlink_"+i,id:"navlink_"+i});
anchorElement.on('click',property.fnctn);
liElement.append(anchorElement);
parentID.append(liElement);
Where property is a JSON object.
property.text is the text that I want to put into anchor element. (Works fine)
I want to attach a click event handler to that anchor element.
The function that needs to be bound to that element is specified in JSON and we can access it like
property.fnctn
The following line should bind the event handler to the anchor element.
anchorElement.on('click',property.fnctn);
This was not working so I tried converting it into string like,
anchorElement.on('click',property.fnctn.toString());
No Success...
When I click on this link, the error is logged in the console
The object has no method 'apply'.
What is the reason...???
I am able to get it working with a slight work around like
anchorElement.attr('onclick',property.fnctn+"()");
Above statement works, but I want to know why .on() API is not working.
Thanks :)
AÐitya.
Update:
Youve said that property.actfn is a string, "paySomeoneClick". It's best not to use strings for event handlers, use functions instead. If you want the function paySomeoneClick, defined in the string, to be called, and if that function is global, you can do this:
anchorElement.on('click',function(event) {
return window[property.fnctn](event);
});
That works because global functions are properties of the global object, which is available via window on browsers, and because of the bracketed notation described below.
If the function is on an object you have a reference to, then:
anchorElement.on('click',function(event) {
return theObject[property.fnctn](event);
});
That works because in JavaScript, you can access properties of objects in two ways: Dotted notation with a literal property name (foo.bar accesses the bar propety on foo) and bracketed notation with a string property name (foo["bar"]). They're equivalent, except of course in the bracketed notation, the string can be the result of an expression, including coming from a property value like property.fnctn.
But I would recommend stepping back and refactoring a bit so you're not passing function names around in strings. Sometimes it's the right answer, but in my experience, not often. :-)
Original answer:
(This assumed that property.fnctn was a function, not a string. But may be of some use to someone...)
The code
anchorElement.on('click',property.fnctn);
will attach the function to the event, but during the call to the function, this will refer to the DOM element, not to your property object.
To get around that, use jQuery's $.proxy:
anchorElement.on('click',$.proxy(property.fnctn, property));
...or ES5's Function#bind:
anchorElement.on('click',property.fnctn.bind(property));
...or a closure:
anchorElement.on('click',function(event) {
return property.fnctn(event);
});
More reading (on my blog):
Mythical methods
You must remember this
Closures are not complicated
I want to access the original event object but the object returns undefined..
$(window).scroll(function(event) {
alert(event.pageX);
});
I'm just trying this out if it will work. The example is as basic as possible so I can work out other events too.
One thing to be careful is not to use the word event as a name of a parameter (or a variable) because in some browsers (like IE and Chrome), event is already an object, and if you use it you will be overwritten the already existing function.
Thus, try changing your code to this :
$(window).scroll(function(ev) {
alert(ev.pageX);
});