javascript adding click events to buttons in a loop - javascript

The code below gets info from xml file.
I succesfully presents the id and name of each planet with a button.
I want to add an onclick event on the button.
Problem now is: it does add the onclick event but only on the last button created in the loop.
What am i doing wrong? Why doesnt it create a onclick event for each button, but only for the last one in loop?
function updatePlaneten() {
var valDiv, planets, valButton, textNode;
// Get xml files
planets = this.responseXML.getElementsByTagName("planeet");
// loop through the <planet> tags
for (var i = 0; i < planets.length; i++) {
valDiv = ''; // clear valDiv each time loop starts
// Get the id and the name from the xml info in current <planet> tag
valDiv += planets[i].getElementsByTagName("id")[0].childNodes[0].nodeValue + "<br>";
valDiv += planets[i].getElementsByTagName("name")[0].childNodes[0].nodeValue + "<br>";
document.getElementById("planetenID").innerHTML += valDiv + "<br>";
// Create button with a value and pass in this object for later reference use (valButton.object=this)
valButton = document.createElement("input");
// valButton.setAttribute("planeetID", planets[i].getElementsByTagName("id")[0].childNodes[0].nodeValue);
valButton.setAttribute("value", 'Meer info');
valButton.setAttribute("type", 'button');
valButton.id = (i + 1);
valButton.object = this;
//
// Here is the problem i cant get fixed
//
//valButton.onclick = function(){ showinfo(); }
valButton.addEventListener('click', showinfo);
// Place the button on screen
document.getElementById("planetenID").appendChild(valButton);
}
}
// simple function to check if it works
function showinfo() {
console.log(this.object);
console.log(this.id);
}

The trouble is this line:
document.getElementById("planetenID").innerHTML += valDiv + "<br>";
When you set innerHTML the content currently in there gets destroyed and replaced with the new html, meaning all your old buttons are now destroyed and new ones are created. The previously attached event listeners do not get attached to the new buttons.
Instead simply create a div/span or whatever container would best help, add your planet text or whatever to it and then use appendChild
valDiv = document.createElement("div");
var id = planets[i].getElementsByTagName("id")[0].childNodes[0].nodeValue;
var name = planets[i].getElementsByTagName("name")[0].childNodes[0].nodeValue;
valDiv.innerHTML = id+"<br>"+name+"<br>";
document.getElementById("planetenID").appendChild(valDiv);
You could also use insertAdjacentHTML
var id = planets[i].getElementsByTagName("id")[0].childNodes[0].nodeValue;
var name = planets[i].getElementsByTagName("name")[0].childNodes[0].nodeValue;
valDiv = id+"<br>"+name+"<br>";
document.getElementById("planetenID").insertAdjacentHTML("beforeend",valDiv);

function updatePlaneten() {
var valDiv, planets, valButton, textNode;
// Get xml files
planets = this.responseXML.getElementsByTagName("planeet");
// loop through the <planet> tags
for (var i = 0; i < planets.length; i++) {
(function(num){
valDiv = document.createElement("div");
// Get the id and the name from the xml info in current <planet> tag
var id = planets[num].getElementsByTagName("id")[0].childNodes[0].nodeValue + "<br>";
var name = planets[num].getElementsByTagName("name")[0].childNodes[0].nodeValue + "<br>";
valDiv.innerHTML = id+"<br>"+name+"<br>";
document.getElementById("planetenID").appendChild(valDiv);
// Create button with a value and pass in this object for later reference use (valButton.object=this)
valButton = document.createElement("input");
// valButton.setAttribute("planeetID", planets[i].getElementsByTagName("id")[0].childNodes[0].nodeValue);
valButton.setAttribute("value", 'Meer info');
valButton.setAttribute("type", 'button');
valButton.id = (num + 1);
valButton.object = this;
// FIX: PASS showinfo TO AN ANONYMOUS FUNCTION CONTAINING THE OBJECT
valButton.addEventListener('click', function(){
showinfo(valButton);
});
// Place the button on screen
document.getElementById("planetenID").appendChild(valButton);
}(i));
}
}
// simple function to check if it works
function showinfo(valButton) {
console.log(valButton.object);
console.log(valButton.id);
}

Related

Button created dynamically cant accses

I am learning javascript, and i am trying to get accses to a button that is created dynamically within a function. The first function createHtml just gets data and creates elements and store them in a variable that i add to the html element and store them in the beerContainer, and that works. I cant however accses the button that is created in the function createHtml. How can i get the alert to pop up when im clicking on saveBeer?
let btn = document.querySelector('#myBtn');
let beerContainer = document.querySelector('#beer-data');
function createHtml(result) {
var html = '';
for(let i = 0; i < result.data.length; i++) {
html += "<h3>"+ result.data[i].name +"</h3>"
html += "<p>"+ result.data[i].description +"</p>"
html += "<button id='save-beer' data-id='"+ result.data[i].id+"'>Save Beer</button>"
}
beerContainer.innerHTML = html;
}
btn.addEventListener('click', function(event) {
fetchApiData(createHtml);
});
var saveBeer = document.querySelector('.save-beer');
saveBeer.addEventListener('click', function(event) {
alert("Hi");
});
try using
var saveBeer = document.querySelector('#save-beer'); // access by id
rather than
var saveBeer = document.querySelector('.save-beer'); // trying to access by class which does not exist
Note the '#' rather than '.' in the querySelector argument

Is there a way to make a function that can be accessed later in a google chrome extension?

I am trying to make an extension that makes a button which then executes a function, however, because the extension executes a javascript file after the page loads. However, when I create the button I want to run a function. Is there a way I can store a function and variables that can be run and access later by the button?
var effect = [[1,100],[2,32], [5,3]];
var points = (function(){var adding = 0;for(var i = 0; i<effect.length;i++){adding+=effect[i][0];};return adding;})()
var score = (function(){var adding = 0;for(var i = 0; i<effect.length;i++){adding+=effect[i][1];};return adding;})()
var percentage=(score/points*100).toString()+"%";
this.effect = effect;
this.points = points;
this.score = score;
var percentage = points/score;
function list_effect(){
var effect_string = "";
for(var i = 0; i<effect.length;i++){
effect_string += ((points-effect[i][0])/score) - ((points)/score);
}
alert(effect_string);
}
if(percentage == 'NaN%'){
// alert('ERROR');
}else{
document.getElementsByClassName("agenda")[0].innerHTML = "<button type=\'button\' id=\'get_list\'>Get List</button>"+ document.getElementsByClassName("agenda")[0].innerHTML;
document.getElementsByClassName("agenda")[0].innerHTML += "<script>" + "this.effect=" + effect.toString() + ";\nthis.points=" + points.toString()+ ";\nthis.score=" + score.toString() +"document.getElementById(\"get_list\").addEventListener(\"onclick\", list_effect());" + "</script>"
}
I have tryied useing this. however that does not work
<script> won't run when added via innerHTML, you should add it using appendChild
normally you don't need to add a <script> element at all, just use createElement and attach the listeners directly
var button = document.createElement('button');
button.id = 'get_list';
button.onclick = function (e) {
// use your variables here directly
};
// clear the previous contents
document.querySelector('.agenda').textContent = '';
// add the button
document.querySelector('.agenda').appendChild(button);

adding an onchange event to select

I have the following js code:
function createConBox() {
var charDiv = document.getElementById("characterList"); // reference to "characterList" div
header = document.createElement("p"); // creates the <p> tag
charDiv.appendChild(header); // adds the <p> tag to the parent node
title = document.createTextNode("Show Only Lines By:"); // creates the text string
header.appendChild(title); // adds the text string to the parent node
// create select box and add elements
selectBox = document.createElement("select");
selectBox.setAttribute("id", "cList");
charDiv.appendChild(selectBox);
charNames = uniqueElemText("h3"); // array of character names
newOption = document.createElement("option");
selectBox.appendChild(newOption);
newOptionTitle = document.createTextNode("Show All Lines");
newOption.appendChild(newOptionTitle);
for (i = 0; i < charNames.length; i++) {
newOption = document.createElement("option");
selectBox.appendChild(newOption);
newOptionTitle = document.createTextNode(charNames[i]);
newOption.appendChild(newOptionTitle);
}
}
function showLines() {
alert("The Box has been changed");
}
Every time the option in the box is changed, I want it to call 'showLines()'. However, every time I try to implement an event, I can only get it to trigger when the page loads, and never again thereafter.
selectBox.onchange = showLines; should solve your problem.
in some browsers onchange get fired only after blurring select box. to over come this you can use onclick instead of onchange
My guess is that you're doing this:
selectBox.onchange = showLines();
If that's the case, just remove the ():
selectBox.onchange = showLines;
When I pass dynamically id in case then what I do:
var selectcell = tablerow.insertCell(1);
var selectelmt = document.createElement('select');
selectelmt.name = 'Select';
selectelmt.value = 'select';
selectelmt.classList = 'form-control input-sm cobclass';
selectelmt.onchange= onselectchange(i);
selectelmt.id = 'cobselect' + i;
selectelmt.options[0] = new Option('select');
selectcell.appendChild(selectelmt);
// ddrbind(i);
show();
i++;`

Dynamicaly Creating a Table with document.createElement and add onclick functions to each tr

I want to add Rows to a Table that already exists and each row has a onclick attribute. The problem is that each row needs to call the function with another parameter. At The moment no matter in what row i click the function is called with the parameter of the last row in the table.
This is how i add the rows to the table :
table = document.getElementById('ProgramTable');
table.style.visibility = "visible";
tableBody = document.getElementById('ProgrammTableBody');
tablelength = jsonObj0.data.map.programs.length;
// Check if there is already a Table, if so
// remove the Table
if (tableexists) {
removetable();
}
for ( var i = 0; i < tablelength; i++) {
channel = jsonObj0.data.map.programs[i].programServiceName;
frequency = jsonObj0.data.map.programs[i].programIdentifier;
imagelink = "../image/image.jsp?context=tuner&identifier="
+ channel;
var row = document.createElement("tr");
row.setAttribute("id", i);
row.onclick = function() {
tuneProgram(frequency)
};
var channelCell = document.createElement("td");
var imageCell = document.createElement("td");
var imageElement = document.createElement("IMG");
var frequencyCell = document.createElement("td");
channel = document.createTextNode(channel);
frequency = document.createTextNode(frequency);
channelCell.appendChild(channel);
frequencyCell.appendChild(frequency);
imageElement.setAttribute("src", imagelink);
imageElement.setAttribute("width", "40");
imageElement.setAttribute("height", "40"); // TODO OnError
// hinzufügen und evtl
// Css Style für Texte
// siehe Tabellencode
imageCell.appendChild(imageElement);
row.appendChild(channelCell);
row.appendChild(frequencyCell);
row.appendChild(imageCell);
tableBody.appendChild(row);
}
So the tune function should be called with the specific frequency parameter but it seems like he is overwriting the onclick parameter everytime so the last one is in there for every row. But why is that so? is he adding the onclick Attribute to every row in that table? I don't get it.
Thanks for your help!
Replace
row.onclick = function() {
tuneProgram(frequency)
};
with
row.onclick = (function(frequency) {return function() {tuneProgram(frequency);};})(frequency);
This "anchors" the value of frequency by creating a new closure for it.
You need to do something like this:
for (var i = 0; i < tablelength; i++) {
(function(i) {
//your code here
})(i);
}
Frequency is being referenced when you click - so if the variable changes, it changes every click element. For example, the first row sets a frequency of one and the last row sets a frequency of two. When the onclick runs it isn't referenced to a value, its referenced to a variable in the chain and gets the current value of two.
because your frequency is a global value, so there is only one frequency that every function refer to it;you can cache it in a closure
something like this:
var programTable = document.getElementById('ProgramTable');
programTable.style.visibility = "visible";
programmTableBody = document.getElementById('ProgrammTableBody');
tablelength = jsonObj0.data.map.programs.length;
if (tableexists) {
removetable();
}
function newTabRow ( table, name, identifier ) {
var link = "../image/image.jsp?context=tuner&identifier=" + name,
row = table.insertRow();
row.innerHTML = '<td>' + name + '</td><td><img width="40" height="40" src="'+link+'" alt="''" /></td><td>'+ identifier +'</td>';
row.onclick = function ( ) {
tuneProgram ( identifier );
}
}
for (var i = tablelength; i-- > 0; ) {
program = jsonObj0.data.map.programs[i];
newTabRow ( programTable, program.programServiceName, program.programIdentifier );
}
Be careful I have a function on top of my page with name "show_field_setting". my function get to value and do something. I have a for loop and in my loop i change 'type' and 'id' for each element. you can see one part inside of my for loop below. finally I add my new element to my div with element id 'my_element_id'. If you want to set a function to your created element you need use something like this:
var new_child = document.createElement('div');new_child.id = id;
new_child.href = "javascript:;";
new_child.onclick = (function (type, id) {
return function() {
show_field_setting (type, id);
};
})(type, id);
document.getElementById('my_element_id').appendChild(new_child);
if you have on argumant in your function only, use this:
var new_child = document.createElement('div');
new_child.href = "javascript:;";
new_child.onclick = (function (your_value) {
return function() {
your_function_name (your_value);
};
})(your_value);
document.getElementById('your_element_id').appendChild(new_child);
finally i don't know why. any way if you are not in loop condition like "while", "for" or even "switch" you can use easy below code line:
var new_child = document.createElement('div');
new_child.href = "javascript:;";
new_child.onclick = function(){your_function_name (your_value_1, your_value_2 , ...)};
document.getElementById('your_element_id').appendChild(new_child);
Have Fun ;) :)

obj is null, javascript

function init()
{
alert("init()");
/**
* Adds an event listener to onclick event on the start button.
*/
xbEvent.addEventListener(document.getElementById("viewInvitation"), "click", function()
{
new Ajax().sendRequest("31260xml/invitations.xml", null, new PageMaster());
xbEvent.addEventListener(document.getElementById("declinebutton"), "click", function ()
{
declineInvitation();
});
});
ok so what I have here is a event listerner function, the case is when viewInvitation is clicked , the program will fetch my xml file and run page master function where I created my decline button with id="declinebutton", however this does not work, the error message that i get is obj=null or the program could not find id = declinebutton, why is it so? I have created it when I called page master using dom. any help will be appreciated.
function PageMaster()
{
this.contentDiv = document.getElementById("content");
}
/**
* Builds the main part of the web page based on the given XML document object
*
* #param {Object} xmlDoc the given XML document object
*/
var subjectList;
var i;
PageMaster.prototype.doIt = function(xmlDoc)
{
alert("PageMaster()");
alert("Clear page...");
this.contentDiv.innerHTML = "";
if (null != xmlDoc)
{
alert("Build page...");
//create div Post
var divPost = document.createElement("div");
divPost.className = "post";
//create h1 element
var h1Element = document.createElement("h1");
var headingText = document.createTextNode("Invitations");
h1Element.appendChild(headingText);
//insert h1 element into div post
divPost.appendChild(h1Element);
subjectList = xmlDoc.getElementsByTagName("subject");
var groupList = xmlDoc.getElementsByTagName("group");
for (i = 0; i < subjectList.length; i++) //for each subject
{
var divEntry = document.createElement("div");
divEntry.className = "entry";
var subjectNum = subjectList[i].attributes[0].nodeValue;
var subjectName = subjectList[i].attributes[1].nodeValue;
var groupId = groupList[i].attributes[0].nodeValue;
var groupName = groupList[i].attributes[1].nodeValue;
var ownerId = groupList[i].attributes[2].nodeValue;
//set up the invitation table attributes
var table=document.createElement("table");
table.width = 411;
table.border = 3;
table.borderColor = "#990000"
var input=document.createElement("p");
var inputText=document.createTextNode("You are invited to join " + groupName + "(groupId : " + groupId +")");
input.className="style11";
var blank=document.createElement("nbps");
input.appendChild(inputText);
var acceptButton=document.createElement("input");
acceptButton.type="button";
acceptButton.id="acceptbutton";
acceptButton.value="accept";
var declineButton=document.createElement("input");
declineButton.type="button";
declineButton.id="declinebutton";
declineButton.value="decline";
table.appendChild(input);
table.appendChild(acceptButton);
table.appendChild(declineButton);
divEntry.appendChild(table);
var blankSpace = document.createElement("p");
divEntry.appendChild(blankSpace);
divPost.appendChild(divEntry);
}
//insert div post into div content
this.contentDiv.appendChild(divPost);
}
};
/**function getValueOf()
{
return i;
}**/
function declineInvitation()
{
alert("decline");
}
function acceptInvitation()
{
alert("hello");
/**var pos=getValueOf();
alert(subjectList[pos].attributes[0].nodeValue);**/
}
That's my page master function, and I definitely have created the button. but it does not work.
Try calling your function like this:
window.onload=init;
The javascript runs as the page loads. At that point, the element does not yet exist in the DOM tree. You'll need to delay the script until the page has loaded.
The example you gave doesn't create the "Decline" button, as your question suggests it should. If it should, you might want to look at that.
Of course, if the button already exists, please disregard this answer.
You have a listener inside a listener. Is that right?
What about this?:
function init(){
alert("init()");
/** * Adds an event listener to onclick event on the start button. */
xbEvent.addEventListener(document.getElementById("viewInvitation"), "click", function()
{
new Ajax().sendRequest("31260xml/invitations.xml", null, new PageMaster());
}
xbEvent.addEventListener(document.getElementById("declinebutton"), "click", function ()
{
declineInvitation();
});
As far as I understand, you create button with id="declinebutton" for each entry from xml, is that right?
If yes, I'd suggest you to generate different id's for each button (for example, append line index to 'declinebutton', so you have buttons 'declinebutton0', 'declinebutton1' an so on), and assign event listener to buttons separately in the loop.

Categories