Javascript classes & event handlers - javascript

I have a problem with this javascript code:
function MyClass() {
var id_nr = Math.ceil(Math.random() * 999999);
this.button_id = 'button_' + id_nr;
}
MyClass.prototype = {
createButton: function() {
var msg = 'Button \'' + this.button_id + '\' was clicked';
var my_button = (
'<input type="button" id="'
+ this.button_id
+ '" value="Click Me" /\>'
);
document.body.innerHTML += '<div>' + my_button + '</div>';
document.getElementById(this.button_id).onclick = function() { alert(msg); }
}
};
window.onload = function() {
var s = new MyClass();
s.createButton();
};
Yes, this current code works fine. But the problem appears when I add more than one MyClass objects:
window.onload = function() {
var s = new MyClass();
s.createButton();
var w = new MyClass();
w.createButton();
/* ...and many other buttons */
};
For some reason the onclick event will be triggered only if I click the button that was created last. And I don't know why.
One workaround could be something like this:
<input type="button" onclick="javascript:doSomeThing();" />
But unfortunately this is not the proper solution right row, because my goal is that the onclik event should be able to call another class methods as well. (Those methods are not created yet).
How can I make this code work properly? Any kind of a help is appreciated.

When you use innerHTML the contents get stripped from the DOM and then readded and parsed again, breaking any past event listener that you might have added.
If you want to do it in vanilla JS, consider using appendChild() and, also, I would suggest addEventListener(). Your code would then look like so:
var my_button = document.createElement('input');
my_button.type = 'button';
my_button.id = this.button_id;
my_button.value = 'Click me';
document.body.appendChild(my_button);
my_button.addEventListener('click', function () { alert (msg) });
Working example with vanilla Javascript
If you are up to using some jQuery, this would be so much easier to implement. Your method would look something like:
var my_button = $('<input>')
.prop('type', 'button')
.prop('id', this.button_id)
.val('Click Me')
.on('click', function () { alert(msg) } );
$('body').append(my_button)
Working example with jQuery
Or you could perhaps use the jQuery.on() method to delegate an event handler to all subsequent instances of your buttons.
Working example with delegated event handler

The use of innerHTML here seems to break the event listener "onclick".
You can avoid this by using the document.createElement method to create your html element :
var but = document.createElement("input");
but.type = "button";
but.id = this.button_id;
but.value = "Click me";
document.body.appendChild(but);
Personaly, I prefer use jQuery for manipulating the DOM tree element, as it offer really powerfull tools for this.

Related

Javascript - Create Dynamic Element then Do Something on Element's Click

How can I call a click event listener on a dynamically created element rendered in the DOM?
I have some scripts that dynamically create elements in the DOM, one of them being a button/a. I would like that button/a to do something once the user clicks it. Right now nothing happens but if I add a setTimeout on the things to happen upon a click, then it kind of works - only let's me do the something on the first element's click (button/a). However I can't rely on a setTimeout to make this chunk of code work.
Here is more or less what I have without the setTimeout method:
// This triggers the whole process
var mainBtn = document.querySelector('.mainBtn');
mainBtn.addEventListener('click', function(e){
e.preventDefault();
mainFunc();
});
// This creates and renders dynamic content in DOM
function mainFunc(){
var out = document.querySelector('.outputWrapper');
var mainArr = ['something ', 'another ', 'else ', 'last one.'];
var div = document.createElement("div");
var btn = document.createElement("a");
var btnText = document.createTextNode("Click Me");
btn.appendChild(btnText);
btn.className = "clickMeBtn";
for(a of probArr){
div.append(a);
div.append(btn);
}
out.append(div);
}
// This is what should happen on button/a click
var clickedBtn = document.querySelector('.clickMeBtn');
if( clickedBtn != null ){
clickedBtn.addEventListener('click', function(e){
e.preventDefault();
console.log('click');
});
}
Here's with the setTimeout method:
// This triggers the whole process
var mainBtn = document.querySelector('.mainBtn');
mainBtn.addEventListener('click', function(e){
e.preventDefault();
mainFunc();
});
// This creates and renders dynamic content in DOM
function mainFunc(){
var out = document.querySelector('.outputWrapper');
var mainArr = ['something ', 'another ', 'else ', 'last one.'];
var div = document.createElement("div");
var btn = document.createElement("a");
var btnText = document.createTextNode("Click Me");
btn.appendChild(btnText);
btn.className = "clickMeBtn";
for(a of probArr){
div.append(a);
div.append(btn);
}
out.append(div);
}
// This is what should happen on button/a click
setTimeout(function(){
var clickedBtn = document.querySelector('.clickMeBtn');
if( clickedBtn != null ){
clickedBtn.addEventListener('click', function(e){
e.preventDefault();
console.log('click');
});
}
}, 10000);
Again this kind of works...it let's me click only on the first instance of the clickedBtn variable.
Any suggestions on how to make this idea work?
Thanks a lot!!
document.querySelector('.clickMeBtn'); returns the first found element, or null.
Attach click event handler when you create the anchor element:
var btn = document.createElement("a");
btn.addEventListener('click', function(e){
e.preventDefault();
console.log('click');
});
I am not 100% sure of what is your question. But the problem I can see is that you cannot bind listeners to elements that are not yet created in the DOM.
So I can see 3 options here:
1- You build a wrapper on top of document.createElement() and a wrapper on top of addEventListener to bind the events to the elements after they are created. For example you build a map of event listeners to begin with, with the 'element selector' as Key and function to call as Value. Then you do a lookup of the listener once the element has been created and you bind it to it with addEventListener.
2- You use JQuery on() method like this:
// define the click handler for all buttons
$( document ).on( "click", "button", function() {
alert( "Button Clicked!" )
});
/* ... some time later ... */
// dynamically add another button to the page
$( "html" ).append( "<button>Click Alert!</button>" );
Source: this JQuery script is from [here][1]
(EDIT) 3- you just bind it after creation, as suggested. Although I thought you wanted to do more advanced stuff, like dynamically add elements asynchronously from the listeners.

JS - if I first run search (highlight text) then .click on section doesn't wanna work

If I run search and highlight text:
$(document).keypress(function(e) {
if(e.which == 13) {
e.preventDefault();
highlightSearch();
}
});
function highlightSearch() {
$('span').removeClass('highlighted');
var text = document.getElementById("query").value;
var query = new RegExp("(\\b" + text + "\\b(?!([^<]+)?>))", "gim");
var e = document.getElementById("searchText").innerHTML;
var enew = e.replace(/(<span class='highlighted'>|<\/span>)/igm, "");
document.getElementById("searchText").innerHTML = enew;
var newe = enew.replace(query, "<span class='highlighted'>$1</span>");
document.getElementById("searchText").innerHTML = newe;
}
then this part of code stop working:
$('.service-box').click(function(){
$('#siteOverlay').addClass('overlay-active');
$('#popupWindow').addClass('service-active');
$('#popupWindow #contentBox').html($(this).html());
});
It doesn't register .click() anymore. I can not find out what is wrong. Can You please help me resolve this?
Thanks!
You are using innerHTML and getting rid of all the event handlers. If you are going to use it, please delegate the events:
$(document).on("click", '.service-box', function(){
$('#siteOverlay').addClass('overlay-active');
$('#popupWindow').addClass('service-active');
$('#popupWindow #contentBox').html($(this).html());
});
Since I don't know what's the static parent, I have used document. Please replace it with a selector for a static parent instead.
The reason why it's not working is because you are using innerHTML for the highlighting, which destroys events of that element and also trigger generation of the DOM over and over again.
Because of this and more reasons I've developed mark.js, a keyword highlighter for search terms or custom regular expressions.

addEventListener after appendChild

I create an element, eltTooltip, with document.createElement etc and add it to the DOM like this (idTooltip contains the id of eltTooltip):
document.body.appendChild(eltTooltip);
var addedElt = document.getElementById(idTooltip);
addedElt.addEventListener("click", function(){...});
Is the click event guaranteed to be added here, or is perhaps the DOM not ready for that?
Could I do this in a better way? (The page is loaded long ago so window.onload can not be used. And I can't use jQuery here.)
Your way works perfectly fine but it's probably better to attach the event listener before you add it to the DOM using eltTooltip. This saves you from fetching the element from the DOM.
Demo
var idTooltip = 'test';
var eltTooltip = document.createElement('div');
eltTooltip.innerHTML = "test"
eltTooltip.setAttribute('id', idTooltip);
eltTooltip.addEventListener("click", function () {
alert('click');
});
document.body.appendChild(eltTooltip);
You could do something like this
window.onload = function (){
var toolTip = document.createElement('div');
toolTip.innerHTML = "someData";
toolTip.addEventListener('click', myfunction);
document.body.appendChild(toolTip);
function myfunction(){
alert("hello guys ");
}
}

Dynamically created form fields keep having the same ID

I dynamically add some text fields to my page with this line of code:
var textboxCount = 0;
$('#addFields').on('click', function(){
var TextField = document.createElement("input");
TextField.setAttribute("type", "text");
TextField.setAttribute("value", textboxCount);
TextField.setAttribute("name", "textbox");
TextField.setAttribute("class", "foo");
TextField.setAttribute("id", "textbox" + textboxCount);
TextField.setAttribute('onkeyup','doSomething('+textboxCount+');'); // for FF
TextField.onkeyup = function() {doSomething(textboxCount);}; // for IE
jQuery('#TextfieldList').append(eleText);
textboxCount += 1; //Increment the count
});
Now I need the unique ID of the field in this function:
function doSomething(id){
alert(id);
}
But when I call the function, I keep getting the same ID with every added field. The value in the textfield is correct though.
Extremely common problem. Change the keyup handler:
TextField.onkeyup = function(textboxCount) {
return function() {
doSomething(textboxCount);}; // for IE
};
}(textboxCount);
(Get rid of the "For FF" line; it's not necessary at all.)
If you don't introduce a new lexical scope somehow, then all of your event handlers will be referring to the exact same "textboxCount" variable. By doing something like what I've shown above (and there are variations), you ensure that each event handler has its own private copy of the counter as it stood at the time the handler was created.
Since you want to get the id of an element in its own event handler you can bypass the whole closure issue by just referencing this.id, where this is the element and id is its id property
TextField.onkeyup = function() {doSomething(this.id);};
You could just use the jQuery library you have in play:
$('#addFields').on('click', function () {
var thisId = $('.foo').length + 1;
var TextField = '<input type="text" name="textbox" class="foo" value="' + thisId + '" id="textbox' + thisId + '">';
jQuery(TextField).appendTo('#TextfieldList');
});
$('#TextfieldList').on('keyup', '.foo', function () {
doSomething($(this).attr('id'));
// or
doSomething(this.id);
});
function doSomething(id){
alert(id);
}
Sample jsFiddle: http://jsfiddle.net/mHT7Z/

Create javascript object with event handler

I have created a JavaScript Object and named it 'Button'. this object has a function that draw a button and append it to specific div element.
var Button = function (id, value) {
this.id = id;
this.value = value;
this.draw = function () {
var element = document.createElement("input");
element.type = "button";
element.id = id;
element.value = value;
document.getElementById("topDiv").appendChild(element);
}
};
I instantiate Button object and call draw() function like this:
var myButton = new Button('btn1', "Test Button");
myButton.draw();
My problem is I cant handle events. I want to connect onclick event to a function. for example:
myButton.onClick = function(){ alert(1); };
but I don't know how to define this.
Try
var Button = function (id, value) {
this.id = id;
this.value = value;
this.draw = function () {
this.element = document.createElement("input");
this.element.type = "button";
this.element.id = id;
this.element.value = value;
document.getElementById("topDiv").appendChild(this.element);
}
};
Button.prototype.addEventListener = function(event, handler){
var el = this.element;
if(!el){
throw 'Not yet rendered';
}
if (el.addEventListener){
el.addEventListener(event, handler, false);
} else if (el.attachEvent){
el.attachEvent('on' + event, handler);
}
}
Demo: Fiddle
I know it's an old question but it's worth mentioning that you could have done it after appending to div:
document.getElementById("topDiv").appendChild(element);
this.element.onclick = function(){ alert(1);};
this is more consistent and much less coding.
jsfiddle
You would have to create your own click() method (which takes a function as a parameter) that binds to the DOM element's click handler. Your draw() method can store a reference to the element in the object instance so that your click() method can access it.
As it was already mentioned, you should attach events to DOM objects.
The simple way is just to expose your DOM element from your custom class:
var Button = function (id, value) {
this.id = id;
this.value = value;
var element = document.createElement("input");
this.element = element;
this.draw = function () {
element.type = "button";
element.id = id;
element.value = value;
document.getElementById("topDiv").appendChild(element);
}
};
Now you can:
var myButton = new Button('btn1', "Test Button");
myButton.draw();
myButton.element.onclick = function(){ alert(1); };
If Native Javascript....
document.getElementById("btn1").onclick
If jQuery
$('#btn1').click(//function(){})....
If jQuery but Button is Created dynamically....
You might try..
$('#btn1').live('click',//function(){ })....
EDIT: As Suggested in the Comment:
Please read what the Documentation says:
As of jQuery 1.7, the .live() method is deprecated. Use .on() to
attach event handlers. Users of older versions of jQuery should use
.delegate() in preference to .live().
This method provides a means to attach delegated event handlers to the
document element of a page, which simplifies the use of event handlers
when content is dynamically added to a page. See the discussion of
direct versus delegated events in the .on() method for more
information.
Rewriting the .live() method in terms of its successors is straightforward; these are templates for equivalent calls for all three event attachment methods:
$(selector).live(events, data, handler); // jQuery 1.3+
$(document).delegate(selector, events, data, handler); // jQuery 1.4.3+
$(document).on(events, selector, data, handler); // jQuery 1.7+
ADDITIONAL
If You can't live without -live-...
As of jQuery 1.4 the .live() method supports custom events as well as
all JavaScript events that bubble. It also supports certain events
that don't bubble, including change, submit, focus and blur.

Categories