I have code like
<a id='lnk1' onclick='do something' >test</a>
Later on code is added to the same anchor tag like
lnk = document.getElementById('lnk1')
lnk.onclick = function() { do something}
Now what is happening is that in the second piece of code the onclick function of the anchor tag is getting overwritten. What I want to happen instead is that the first onclick's code is run and after that the 2nd onclick's is run.
There is a very simple, standards-compliant way to do this:
lnk1.addEventListener('click', function() {
// do something
});
This doesn't work in IE before version 9, so you'll need to do this:
var handler = function() {
// do something
};
if ("addEventListener" in lnk1) { // standards-compliant browsers
lnk1.addEventListener('click', handler);
} else { // Internet Explorer < v9
lnk1.attachEvent('onclick', handler);
}
This will work, and both the original function specificed in the HTML attribute and in the code above will run. HOWEVER it would be far nicer to define all your event handlers in the same place: in the Javascript. Think hard about removing event handling logic from your HTML attributes.
You could try this:
var lnk = document.getElementById('lnk1'); // don't forget var!
var oldHandler = lnk.onclick;
lnk.onclick = function(ev) {
if (oldHandler) oldHandler(ev);
// do something ...
};
That code saves a reference to the old handler, and if it's not empty it calls it before doing whatever else the new handler wants to do.
You could put the call to the old handler after the new code, or mixed in, or whatever.
<a id='lnk1' onclick='do something' >test</a>
JavaScript
lnk1 = document.getElementById('lnk1')
lnk1.addEventListener('click',function() { do something}, false);
when setting onclick you are overwrite existing attribute, but assign click through event listener then it will be ok.
You have a mistake in your statement:
JAVASCRIPT
lnk = document.getElementById('lnk1')
lnk1.onclick = function() { do something} \\ replace lnk1 with lnk
lnk.onclick = function() { do something} \\ this will work
You have defined lnk as variable but your are calling lnk1 with onclick event. This is a wrong statement.
USE HTML
<a id='lnk1'>test</a>
See the Demo: http://jsfiddle.net/tm5cX/
Related
Please refer to my previous question: How to get element in template html?.
Firstly, I follow the solution in How to get element in template html? to obtain the element.
var template = templateContent.content;
Then I try to bind a click event for searchBtn:
searchBtn.addEventListener('click', function() {
console.log('click'); // does not work
});
This binding fails, so I use the event delegation to handle event:
document.addEventListener('click',function(e){
if(e.target && e.target.id== 'searchBtn'){
console.log('click');
console.log(template.querySelector('#results'));
template.querySelector('#results').innerText = 'Foo';
}
});
The two logs are expected, but the last command to update the innerText does not work. On the other hand, I can update its content outside the click event. Any solution?
The final version of my code:
var link = document.querySelector('link[rel="import"][href="search.html"]');
var templateContent = link.import.querySelector('template');
var template = templateContent.content;
var searchBtn = template.querySelector('#searchBtn');
console.log(searchBtn);
template.querySelector('#results').innerText = 'Bar'; // ok
document.addEventListener('click',function(e){
if(e.target && e.target.id== 'searchBtn'){
console.log('click'); // ok
console.log(template.querySelector('#results')); // ok
template.querySelector('#results').innerText = 'Foo'; // does not work
}
});
Updates
Now I retrieve the results element outside the function:
var results = template.querySelector('#results');
Then inside the function, results.innerText = 'Foo'; works. However, this only updates the results itself, and the UI is not updated correspondingly.
How to update the UI inside the template?
You got it right, it is the binding issue. template is not available inside click function. You need to bind this. There are many ways to do this e.g.
document.addEventListener('click',onClick.bind(this));
onClick = function(e){
if(e.target && e.target.id== 'searchBtn'){
console.log('click'); // ok
console.log(template.querySelector('#results')); // ok
this.template.querySelector('#results').innerText = 'Foo'; // should work
}
}
There are many ways to get the HTML elements
You can get use
var sbtnElement = document.getElementById('searchBtn');
then add eventlistener to it
sbtnElement.addEventlistener('click', function() {
console.log('click'); // does not work
});
Naturally getElement by id will give you one element, and getElementsByClassName will return you an array of elements with the same classname. Same goes for getElementsByTagName.
hi i don't know what are you try to create for. but have you ever try to use template in script ? like this one
<script id="sampleTemplate" type="text/template">
// do the html here
</script>
So in that case you can bind event using that one. just a normal creating of html and binding javascript function.
You can refer to this blog. https://jonsuh.com/blog/javascript-templating-without-a-library/
I hope, i helped you thru this. :)
Currently working on a small project using an OLOO style approach.
Problem found here
So the issue I am facing is that I have an event handler.
eventHandler: function() {
console.log('Hit');
testSelector.removeEventListener('click', this.eventHandler, false);
}
Now what happens is that I want this to be removed after the first click. However this does not seem to work as I expected. I am binding the object this reference yet there seems to be something that I am missing in what is actually going on here. Would anyone be able to clarify what is actually happening or where I went wrong?
I'm not an expert in OLOO, but I can see two issues in your example:
the this inside an eventListener callback handler refers to the node so you need to take care you're referencing the same this in both methods ( add/removeEventListener )
removeEventListener won't work if the listener parameter isn't the same as addEventListener, and when you use bind you're actually creating a new function (so you have to keep track of that to)
in code:
var testSelector = document.querySelector('.test');
var object = {
init: function() {
this.ref = this.eventHandler.bind(this)
testSelector.addEventListener('click', this.ref, false);
},
eventHandler: function() {
console.log('Hit');
testSelector.removeEventListener('click', this.ref, false);
}
}
object.init();
https://jsbin.com/hejenuraba/1/edit?js,console,output
I got it to work in my environment as follows:
var testSelector;
var eventHandler = function(){
console.log('Hit');
testSelector.removeEventListener('click', eventHandler, false);
}
$(document).ready(function(){
testSelector = this.getElementById('btn');
testSelector.addEventListener('click',eventHandler);
});
You code looks fine but you may want to cross check the following:
In your line of code:
testSelector.removeEventListener('click', this.eventHandler, false);
Make sure you have the references to testSelector and eventHandler
I'm working in a javascript based system with some legacy code (ominous music), and this legacy code adds event listeners like this
foo.addEventListener("click",function(){
//do some stuff
});
Is there a way for me to programmatically remove event listeners that have been added like this? I know about removeEventListener, but's it's not clear from the documentation how (if at all) to remove a listener which a programmer added via an anonymous function.
As far as I can tell, you can't use an anonymous function if you want to call removeEventListener because you need a reference to the same function that you used with addEventListener and the only way to do that is to have a name for the function (e.g. not anonymous).
Similar question and conclusion here: removeEventListener on anonymous functions in JavaScript
Without changing the structure of your code, you can give it a name and then use that name later.
foo.addEventListener("click", function fooClickHandler(){
//do some stuff
});
// then later
foo.removeEventListener("click", fooClickHandler);
You can remove them the same way that you add them by passing in the handler that you created it with. The handler ceases to be an anonymous function at this point though:
var handler = function() {
// do some stuff
};
foo.addEventListener("click", handler);
foo.removeEventListener("click", handler);
You can do some funky stuff like this to remove handlers with anonymous functions although I don't recommend it, but it is possible and you can't access callee in strict mode:
document.addEventListener('click', function(e) {
console.log('click');
if(e.unbind) {
document.removeEventListener('click', arguments.callee);
}
});
var event;
// First actual real event
event = new Event('click');
document.dispatchEvent(event);
// Unbinding event
event = new Event('click');
event.unbind = true;
document.dispatchEvent(event);
// Should not fire
event = new Event('click');
document.dispatchEvent(event);
If you want to get rid of all eventlisteners before adding your own you can use clonenode and remove original. The copy will not have the eventlisteners copied with it.
var fee = foo.cloneNode();
foo.parentNode.replaceChild(fee, foo);
Should look the same but without eventlistener.
Got a fiddle here that proves my point: http://jsfiddle.net/U7w7M/1/
Notice how the formatting stays, the position is the same, but click action is removed after first click
This is my html code
Hit
This is my javascript file
function clickHandler(evt) {
var thisLink = (evt)?evt.target:Window.event.srcElement;
alert(thisLink.innerHTML);
return false;
}
But when i click the Hit Link, it redirects.
you need to pass in the event if you wish to preventDefault.
html:
Hit
script:
function runFunction (evt) {
evt.preventDefault();
evt.stopPropagation();
}
To tie both of the very-correct answers together, what's happened is you've inlined a function where you've written onclick="return runFunction();"
If you look at that, what it's really doing is going like this:
var link = document.getElementById("myLink");
link.onclick = function () { runFunction(); };
See the problem?
My runFunction is being called without any event object passed in, at all.
...which means that var thisLink = (evt) ? is going to return false, which means that it's going to try to run in oldIE-mode.
By writing onclick="runFunction", that's the same as saying:
link.onclick = runFunction;
Which means that when the onclick event happens, runFunction will be called, and in W3C-compliant browsers, it will be sent an event object.
Which is why that solution works.
The best way to avoid a lot of this confusion is to deal with JavaScript from inside of JavaScript, and to deal with HTML inside of HTML, so that you don't have to worry about how strings translate into code.
Now, to get all of this to work, AND prevent redirection, you want to do this:
for W3C browsers (the ones that pass the event parameter):
function runFunction (evt) {
// stops the default-action from happening
// means you need to find another way to fire it, if you want to later
evt.preventDefault();
// stops higher-up elements from hearing about the event
// like if you stop a submit button from "clicking", that doesn't stop the form
// from submitting
evt.stopPropagation();
//the oldIE versions of both of these are
event.cancelBubble = true;
event.returnValue = false;
}
When I plugged your code into chrome, I got this as the error in the console:
Uncaught TypeError: Cannot read property 'srcElement' of undefined
IF the javascript bombs out while processing, it never gets a chance to return at all so the browser tends to disregard what is in the onclick handler after the exception.
Since it bombed out... default behavior of anchor tags, which is to send you off to wherever the href says to go.
Try wrapping the contents of the function in a try/catch block and see what turns up if this kind of thing plagues you.
I am writing a JS which is used as a plugin. The JS has an onbeforeunload event.
I want suggestions so that my onbeforeunload event doesn't override the existing onbeforeunload event (if any). Can I append my onbeforeunload to the existing one?
Thanks.
I felt this has not been answered completely, because no examples were shown using addEventListener (but The MAZZTer pointed out the addEventListener solution though). My solution is the same as Julian D. but without using jQuery, only native javascript.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Before Unload</title>
</head>
<body>
<p>Test</p>
<script>
window.addEventListener('beforeunload', function (event) {
console.log('handler 1')
event.preventDefault()
event.returnValue = ''
});
window.addEventListener('beforeunload', function (event) {
console.log('handler 2')
});
</script>
</body>
</html>
In this example, both listeners will be executed. If any other beforeunload listeners were set, it would not override them. We would get the following output (order is not guaranteed):
handler 1
handler 2
And, importantly, if one or more of the event listener does event.preventDefault(); event.returnValue = '', a prompt asking the user if he really wants to reload will occur.
This can be useful if you are editing a form and at the same time you are downloading a file via ajax and do not want to lose data on any of these action. Each of these could have a listener to prevent page reload.
const editingForm = function (event) {
console.log('I am preventing losing form data')
event.preventDefault()
event.returnValue = ''
}
const preventDownload = function (event) {
console.log('I am preventing a download')
event.preventDefault()
event.returnValue = ''
}
// Add listener when the download starts
window.addEventListener('beforeunload', preventDownload);
// Add listener when the form is being edited
window.addEventListener('beforeunload', editingForm);
// Remove listener when the download ends
window.removeEventListener('beforeunload', preventDownload);
// Remove listener when the form editing ends
window.removeEventListener('beforeunload', editingForm);
You only need to take care of this if you are not using event observing but attach your onbeforeunload handler directly (which you should not). If so, use something like this to avoid overwriting of existing handlers.
(function() {
var existingHandler = window.onbeforeunload;
window.onbeforeunload = function(event) {
if (existingHandler) existingHandler(event);
// your own handler code here
}
})();
Unfortunately, you can't prevent other (later) scripts to overwrite your handler. But again, this can be solved by adding an event listener instead:
$(window).unload(function(event) {
// your handler code here
});
My idea:
var callbacks = [];
window.onbeforeunload = function() {
while (callbacks.length) {
var cb = callbacks.shift();
typeof(cb)==="function" && cb();
}
}
and
callbacks.push(function() {
console.log("callback");
});
Try this:
var f = window.onbeforeunload;
window.onbeforeunload = function () {
f();
/* New code or functions */
}
You can modify this function many times , without losing other functions.
If you bind using jQuery, it will append the binding to the existing list, so there is no need to worry.
From the jQuery Docs on() method:
As of jQuery 1.4, the same event handler can be bound to an element
multiple times.
function greet(event) { alert("Hello "+event.data.name); }
$("button").on("beforeunload", { name: "Karl" }, greet);
$("button").on("beforeunload", { name: "Addy" }, greet);
You can use different javascript frameworks like jquery or you could probably add a small event add handler to do this. Like you have an object thatcontains a number of functions that you have added and then in the onbefore unload you run the added functions. So when you want to add a new function to the event you add it to your object instead.
something like this:
var unloadMethods = [];
function addOnBeforeUnloadEvent(newEvent) { //new Event is a function
unloadMethods[unloadMethods.length] = newEvent;
}
window.onbeforeunload = function() {
for (var i=0; i<unloadMethods.length; i++) {
if(typeof unloadMethods[i] === "function") {unloadMethods[i]();}
}
}
Those frameworks mentioned use addEventListener internally. If you are not using a framework, use that.
https://developer.mozilla.org/en-US/docs/DOM/element.addEventListener
For older versions of IE you should have a fallback to use attachEvent instead:
http://msdn.microsoft.com/en-us/library/ie/ms536343(v=vs.85).aspx
I liked Marius's solution, but embellished on it to cater for situations where the var f is null, and to return the first string returned by any function in the chain:
function eventBeforeUnload(nextFN){
//some browsers do not support methods in eventAdd above to handle window.onbeforeunload
//so this is a way of attaching more than one event listener by chaining the functions together
//The onbeforeunload expects a string as a return, and will pop its own dialog - this is browser behavior that can't
//be overridden to prevent sites stopping you from leaving. Some browsers ignore this text and show their own message.
var firstFN = window.onbeforeunload;
window.onbeforeunload = function () {
var x;
if (firstFN) {
//see if the first function returns something
x = firstFN();
//if it does, return that
if (x) return x;
}
//return whatever is returned from the next function in the chain
return nextFN();
}
}
In your code where required use it as such
eventBeforeUnload(myFunction);
//or
eventBeforeUnload(function(){if(whatever) return 'unsaved data';);