Unexpected reference to variable in anonymous function - javascript

The following JavaScript has some unwanted behaviour:
<html>
<script>
function AddEventListener(el, listener)
{
el.addEventListener ? el.addEventListener('click', listener) :
el.attachEvent('onclick', listener);
}
function Init(parent)
{
var span = document.createElement("span");
span.innerText = "Span1";
AddEventListener(span, function() { alert(span.innerText); } );
parent.appendChild(span);
var span = document.createElement("span");
span.innerText = "Span2";
parent.appendChild(span);
}
</script>
<body onload="Init(document.getElementById('drop'));">
<div id='drop'></div>
</body>
</html>
If you click on Span1, Span2 is shown in the alert window. I understand why: javascript variable scope. But I don't know how to solve it.
Some context: I used this.innerHTML which works fine except in IE8. this points to the window in IE8, not to the parent of the event listener.

Instead of using a global variable in your click callback, you can use the event parameter that is being passed to every event you register, and then use its event.target parameter to get the DOM element that participated in the event.
For that, you may change your event listener registering to: AddEventListener(span,function(event) {alert((event.target ? event.target : event.srcElement).innerText);});
I've set up an example of it using your code here: http://jsfiddle.net/dvirazulay/fTa6T/1/

You can return a function instead of just declaring one, but I like #Dvir Azulay suggestion better:
function CreateAlert(span)
{
return function() { alert(span.InnerText); };
}
function Init(parent)
{
var span=document.createElement("span");
span.innerText="Span1";
AddEventListener(span, CreateAlert(span));
parent.appendChild(span);
var span=document.createElement("span");
span.innerText="Span2";
parent.appendChild(span);
}

Try this: (changed the second definition of span to span2)
<!DOCTYPE html>
<html>
<script>
function AddEventListener(el,listener)
{
el.addEventListener ? el.addEventListener('click', listener) : el.attachEvent('onclick', listener);
}
function Init(parent)
{
var span=document.createElement("span");
span.innerText="Span1";
AddEventListener(span,function() {alert(span.innerText);});
parent.appendChild(span);
var span2=document.createElement("span");
span2.innerText="Span2";
parent.appendChild(span2);
}
</script>
<body onload="Init(document.getElementById('drop'));">
<div id='drop'></div>
</body>
</html>

Related

Removing an onclick event from a div with javascript

Basically what the title says this is the code that I've tried but it doesn't work:
<div id="box" onclick="doSmt(var1, bar2)">
if (condition){
box.removeEventListener("click" , doSmt)}
I think it's better if you remove the onclick event instead of that attempt
//onclick function
function doSmt(){
console.log("something");
}
//remove onclick event, this will be inside your if condition
document.getElementById('box').removeAttribute("onclick");
<div id="box" onclick="doSmt()"> div</div>
What what I read at MDN for removeEventListener you can't remove an event listener that is part of the HTML attribute. So there's two options:
Add the event listener on page load
onClickHandler = () => doSmt(var1, var2);
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('box').addEventListener('click', onClickHandler);
});
// and later
if (condition) {
document.getElementById('box').removeEventListener('click', onClickHandler)
Or if you can't modify the HTML you could modify doSMT to watch for a disabled bit.
let disableBox = false;
function doSmt() {
if (disableBox) return;
// ...
}
if (condition) {
disableBox = true;
}
Or
it can be removed by first accessing the element and then setting the attribute to null
<div id="myDiv" onclick="handleClick()">Click me</div>
<script>
function handleClick() {
alert("Div was clicked!");
}
// Remove the onclick event from the div
const div = document.getElementById("myDiv");
div.onclick = null;
</script>

Disable href after one click

I'm trying to disable an html link after one click.
I found some solutions but it seems that they doesn't work on my code.
Here is what I tried to do:
<html>
<script type="text/javascript">
function image(url)
{
var img = document.createElement("IMG");
var url = "http://www.luigimelisi.com/wp-content/uploads/2014/03/url_routing.jpg";
img.src = url;
document.getElementById('image').appendChild(img);
}
function clickAndDisable(link)
{
link.onclick = function(event)
{
e.preventDefault();
}
}
</script>
<body>
<div id="image"></div>
<div>Click Here</div>
</body>
</html>
How can I fix it?
In your clickAndDisable function and the onclick handler, the event (in this case 'e') is not being passed as a parameter. You pass in 'event' but reference 'e'. You can make this work by changing 'e' to 'event'. This is what your code might look like for that to work:
link.onclick = function(event) {
event.preventDefault();
};
Actually, your code will work in the way you are hoping. You just made a typo. The mistake you made is in the function 'clickAndDisable' you handle an event by passing in a parameter 'event', but then you try to utilize the event by calling e.preventDefault.
to fix, change e.preventDefault() to event.preventDefault()
Here is the working code or test on JSFiddle here: https://jsfiddle.net/y6ktL4af/
<html>
<script type="text/javascript">
function image(url)
{
var img = document.createElement("IMG");
var url = "http://www.luigimelisi.com/wp-content/uploads/2014/03/url_routing.jpg";
img.src = url;
document.getElementById('image').appendChild(img);
}
function clickAndDisable(link)
{
link.onclick = function(event)
{
event.preventDefault();
}
}
</script>
<body>
<div id="image"></div>
<div>Click Here</div>
</body>
</html>
I think you can fix this by removing the href property of the a tag, and then modifying the clickAndDisable() function to do something like:
link.onclick = null;
image();
There are several ways to solve this with disabling or hiding the link but a reusable one would be to attach an event handler to all links that should be clicked only once:
var oneClickEls = document.querySelectorAll('[data-allow-one-click="true"]');
var _i, _len, _el;
for ( _i = 0, _len = oneClickEls.length; _i < _len; _i++ ) {
_el = oneClickEls[_i];
_el.hasBeenClicked = false;
_el.addEventListener('click', function(event) {
if (this.hasBeenClicked) {
event.preventDefault();
return;
}
this.hasBeenClicked = true;
});
}
This will allow us to specify which links should only be clicked one in the html by adding an attribute named data-allow-one-click:
Click Here
Here is a js fiddle showing your code working: https://fiddle.jshell.net/1akdp6sp/
A possible method would be to change the onclick statement in the a element to something like:
onclick="this.href=''"
This changes the href of the hyperlink to nothing, so nothing happens when the link is clicked on. It also obviates the need for the clickAndDisable function. Is that what you mean by disabled?

Why is the button disapearing?

The text "Now I'm here..." is supposed to disappear when the button is clicked, not the button itself.
<div id="alpha">Now I'm here...</div>
<button type="button" onclick="remove()">Remove</button>
<script>
function remove()
{
var element = document.getElementById("alpha");
element.parentNode.removeChild(element);
}
/*function add()
{
var ele = document.createElement("p");
var text = document.createTextNode("This is new text");
ele.appendChild(text);
var location = document.getElementById("alpha");
location.appendChild(ele);
}*/
</script>
There is another function called remove that is interfering with your function.
Rename your function and it works fine:
http://jsfiddle.net/fL3gZ/
<div id="alpha">Now I'm here...</div>
<button type="button" onclick="Myremove()">Remove</button>
<script>
function Myremove()
{
var element = document.getElementById("alpha");
element.parentNode.removeChild(element);
}
</script>
What's happening is remove() is being called on the button itself! HTMLElement.prototype.remove is an existing function (in some browsers)! Oh god!
var button = document.getElementsByTagName("button")[0];
// surprise! this is what's actually happening
button.remove();
Check out this alternative approach. See: fiddle
Change HTML to
<div id="alpha">Now I'm here...</div>
<button type="button">Remove</button>
Then use this JavaScript
function remove(id) {
var elem = document.getElementById(id);
if (elem) elem.parentNode.removeChild(elem);
}
var button = document.getElementsByTagName("button")[0];
button.addEventListener("click", function(event) {
remove("alpha");
event.preventDefault();
});
A couple things about this:
I'm favoring a more unobtrusive approach
The remove function is single purpose, and reusable
It will work in more browsers
You won't run into WTFs like you just experienced
remove() is already an excisting javascript method, so you are actually calling that method on your button instead of calling the function.
Just rename the function and it will be fine.
Fiddle: http://jsfiddle.net/WkUqT/7/
function removeText()
{
var element = document.getElementById("alpha");
element.parentNode.removeChild(element);
}
You are probably using chrome as your browser to test that code. Elements in chrome have a self-removal, .remove() method which removes the element itself from its container.
This is the main reason why the code above removes the button, because of this the appended event in your onclick() declaration was not invoked because the element invoking the event does not exist anymore. Try changing the name of your function to removeElement().

Override javascript click event one time

I would like to replace the default action of an click event for all anchors in a webpage.
When I use this piece of code:
<html> <head> <script>
var list=document.getElementsByTagName("a");
var isChecked = false;
function load () {
for (i=0; i<list.length; i++)
{
var old = (list[i].onclick) ? list[i].onclick : function () {};
list[i].onclick = function () {
if( !isChecked)
{
test();
old();
}
else
old();
};
}
}
function test() {
alert("new action");
isChecked = true;
}
</script> </head>
<body onload="load();">
<a id="nr_1" onClick="alert('test');"> Anchor 1 </A>
<a id="nr_2" onClick="alert('test2');"> Anchor 2 </A>
</body> </html>
When I click an anchor I get the alert out of the test function and then the default function of the second anchor (even when I click the first anchor). When I then again click one of the two anchors I always get the Alert from the second anchor.
How do I put the original onclick functions back for each anchor element? When someone has an solution in jquery I would be glad as well.
EDIT
I was succesfull using this code:
function load()
{
$('a').attr('disabled', 'disabled');
$('a').click(function(e){
if($(this).attr('disabled'))
{
alert("new");
e.preventDefault();
$('a').removeAttr("disabled");
this.click();
}
});
}
On loading of the page this function is called giving all anchor elements a "disabled" attribute. After clicking the element the e.preventDefault() function disables the inline onclick function. Then I remove the "disabled" attribute and call the click function of the element again. because now the element doesn't have a "disabled" attribute only the default function is performed. I'm still open for "more elegant" solutions to this problem, but for now I'm a happy camper!
If you use jQuery you can combine a one-time handler with a persistent handler:
Documentation for .one() and .on()
Working Example: http://jsfiddle.net/Q8gmN/
Sample HTML:
<input type="button" id="click" value="click" />
​
Sample JavaScript:
button.one('click', function () {
console.log('one time function fired');
});
button.on('click', function () {
console.log('persistent function fired');
});
​

How can I attach event to a tag which is in string form?

I'm creating html on runtime like this:
var myVar = "<div id='abc'>some clickable text</div>"
Now, I want to attach some event, say onclick, to this div. How can I do that on next line? I'll add this to DOM later.
PS: I've to accomplish this without using JQuery.
Instead of building your div as a string, you'll want to use document.createElement('div'). This way you will have a real dom object, and can get and set it's propeties, including onClick
Will this help? Since you dynamically generate it, you know the control id of the DIV.
document.getElementbyId('abc').onClick = foo;
function foo()
{
alert("All your impl to go here");
}
Try building the div as a DOM element first.
var myVar = document.createElement("div"),
parentDiv = document.getElementById("parent_div");
parentDiv.appendChild(myVar);
myVar.innerHTML = "some clickable text";
if(myVar.addEventListener){
myVar.addEventListener("click", clickFn, false);
}
else if(myVar.attachEvent){
myVar.attachEvent("onclick", clickFn);
}
else{
myVar.onclick = clickFn;
}
The addEventListener method is standard, but not every browser plays nice with the standard.
EDIT: As mentioned, an element must be added to the DOM first.
Or you can use this technique: attach event to the document.body. Then if the event target is not the needed div than just do nothing. It is the same techinque jquery uses for its live function:
// crossbrowser event attachment function
function listen(evnt, elem, func) {
if (elem.addEventListener) {
elem.addEventListener(evnt, func, false);
}
else if (elem.attachEvent) {
var r = elem.attachEvent("on" + evnt, func);
return r;
}
else window.alert('I\'m sorry Dave, I\'m afraid I can\'t do that.');
}
// this is an analog of the jquery.live
var assignLiveEvent = function(id, evnt, callback) {
var handler = function(e) {
e = e || window.event;
e.target = e.target || e.srcElement;
if (e.target.id == id) {
//your code here
callback(e);
}
};
listen(evnt, document.body, handler);
};
var myVar = "<div id='abc'>some clickable text</div>";
assignLiveEvent("abc", "click", function(e) {
//your code here
});
// now you can add your div to DOM even after event handler assignation
Here is demo.
Brian Glaz is totally right but, if for some reason, you really need to do it this way, you have two options:
you can only add events to something that is already in the DOM, using pure javascript, so you would have to include it in the html like:
document.body.innerHTML += myVar;
and then, attach the event with
document.getElementById('abc').addEventListener('click', function(e){
//your code
}, 1);
With jQuery, you could use .live() to attach events to elements that are not yet present in the DOM:
$('#abc').live('click', function(e){
//your code here
});
so you could add the div later...

Categories

Resources