I have 2 files index.html
and all.js
.
all.js
(function(){
if (document.getElementById('btn1')){
document.getElementById("btn1").addEventListener("click", displayMessage);
}
function displayMessage(){
alert("Hi!");
}
})()
index.html
Example 1: - working example
<button id="btn1">Hit me !</button>
<script type="text/javascript" src="all.js"></script>
Example 2: - not working example
<button id="btn2" onclick="show()">Show</button>
<script type="text/javascript">
function show(){
var btn = document.createElement("BUTTON");
btn.setAttribute("id", "btn1");
btn.innerHTML = "Hit me !";
document.getElementById("btn2");
document.body.insertBefore(btn, btn2);
}
</script>
<script type="text/javascript" src="all.js"></script>
Description:
In the first example btn1
was rendered right away and the event was attached to it. In the second example btn1
is rendered only when you click on btn2
and after that if you click on btn1
nothing will happen.
How can I attach the event to btn1
without changing all.js
?
Here is the entire code repository
Note: Don't ask my why I try to do something like this, don't ask me why I am not allowed to change all.js
because this example is a simplified one, in my case all.js
file contains thousands of lines with lots of events on many elements. JavaScript solution means that I am not allowed to use other external libraries.
UPDATE: Answer acceptance
I got a lot of different answers. Some of you worked harder to solve this issue, some of you worked less but the good thing is that I decided. I got some working solutions but some of them worked for only this particular case without taking in consideration that my real all.js file has 4029 lines of code, some solutions suggested to use event delegation which I agree but unfortunately I can not change my all.js file now. In the end I picked the best solutions, I've also considered who answered first, I've taken into consideration also who put a lot of work into this, etc. Anyways, the solution that I'm gonna implement is a merge between 2 solutions, Aruna's and Vlacav's solution (+ some changes by my self) and here it is:
function show(){
var btn = document.createElement("BUTTON");
btn.setAttribute("id", "btn1");
btn.innerHTML = "Hit me !";
document.getElementById("btn2");
document.body.insertBefore(btn, btn2);
resetJs('all.js');
}
function resetJs(path) {
var scripts = document.getElementsByTagName('script')
, newScript = document.createElement("script");
newScript.src = path + "?timestamp=" + Math.round(new Date().getTime()/1000);
for (var i = 0; i < scripts.length; ++i) {
var srcUrl = scripts[i].getAttribute('src');
if (srcUrl && srcUrl.indexOf(path) > -1) {
scripts[i].parentNode.replaceChild(newScript, scripts[i]);
}
}
}
Unfortunately I can't split the reputation and I have to give it to only one of them and I want to give this to Vlacav because he was the first one who posted the the solution that I was searching for and he also did some changes on it when I asked him to do so. I also rated up Aruna's answer because he deserve this.
I've posted the solution on a separate branch here and I also created several other branches on this repo with the other solutions that I received because they might be useful in other situations.
You have to reload
all.js
after each new element. That is the only solution I can think of.After action that creates an element you'll call a function:
this shall reevalute
all.js
and listeners fromall.js
shall attach to elements being in DOM.This is very inefficient solution, so if you make several dynamic updates, reload
all.js
at the end of all updates, not after every single update.This also mean, that some statefull variables from
all.js
might be reinitialized, counters may be reset to zero, etc.I would personally never do this kind of piggy patching, but if you have no other choice, give it a try.
Edit
Tested working code
querySelector
andDate.now()
are IE9+ compatible, for support IE8- the code must be modified.Update
IE8- compatible version (tested in IE7)
Note: IE8 does not support
addEventListener
(it has very similar methodattachEvent
), so if yourall.js
relies onaddEventListener
, thenall.js
is only IE9+ compatible.