JavaScript onclick functions unbound due to innerHTML usage - javascript

I have a function in javascript that I intend to use to create/add a link to a 'subLink' div, and add an onclick event to this link that loads associated 'content' to a contentDiv:
function addSubLink(text, name, content) {
// append programLink span to subLinks:
document.getElementById("subLinks").innerHTML += "<span class=\"programLink\" id=\"" + name + "\">" + text + "</span>";
// load program content onclick:
document.getElementById(name).onclick = function () {
// set value of programContent to content's value
document.getElementById("programContent").innerHTML = content;
}
}
This function would be called, for example, by loadAboutPage() to populate the subLink div
var whatContent = "<p>A website!</p>";
var whyContent = "<p>Recreation!</p>";
var howContent = "<p>Kludges.</p>";
addSubLink("WHAT", "whatLink", whatContent);
addSubLink("WHY", "whyLink", whyContent);
addSubLink("HOW", "howLink", howContent);
The problem is that only the last subLink has an onclick event attached to it (content is loaded and css class changed). That is, it creates WHAT, WHY, and HOW links, but only appends the onclick function to the last called: HOW, in this case.
I'm very rusty when it comes to JavaScript, so I have no idea if it's a result of my lack of knowledge about anonymous functions, or using the local 'name' variable incorrectly, or anything else completely different. I've searched for awhile, but it seems I'm too ignorant to even figure out what a similar problem would be!
Anyway, I greatly appreciate your help! Thanks in advance!
EDIT:
Here's a complete HTML example:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Stack Overflow Example</title>
<script type="text/javascript">
function addSubLink(text, name, content) {
document.getElementById("subLinks").innerHTML += "<span class=\"programLink\" id=\"" + name + "\">" + text + "</span>";
document.getElementById(name).onclick = function () {
document.getElementById("program").innerHTML = content;
}
}
window.onload = function() {
var whatContent = "<p>A website!</p>";
var whyContent = "<p>Recreation!</p>";
var howContent = "<p>Kludges.</p>";
addSubLink("WHAT", "whatLink", whatContent);
addSubLink("WHY", "whyLink", whyContent);
addSubLink("HOW", "howLink", howContent);
}
</script>
</head>
<body>
<div id="container">
<div id="programWindow">
<div id="subLinks"> </div>
<div id="program"> </div>
</div>
</div>
</body>
</html>

The issue is due to:
....innerHTML += "...";
By setting innerHTML, all existing child elements are first removed, then the markup is parsed to create new elements. So, while the original "WHAT" and "WHY" spans did have onclick bindings, they've being replaced by similar elements that don't.
To append a new element and keep state, you'll want to use DOM methods like createElement(), appendChild(), and createTextNode() (or set textContent/innerText):
function addSubLink(text, name, content) {
var span = document.createElement('span');
span.className = 'programLink';
span.id = name;
span.appendChild(document.createTextNode(text));
// append programLink span to subLinks:
document.getElementById('subLinks').appendChild(span);
// load program content onclick:
span.onclick = function () {
// set value of programContent to content's value
document.getElementById("programContent").innerHTML = content;
};
}
Example: http://jsfiddle.net/bG7Dt/

Related

HTML select options do not get populated with getElementById and innerHTML [duplicate]

I am attempting to create a bit of JavaScript that, on the click of a button, adds a tag filled with options. The options will be defined with an array called "roster". What I would like to see is a dropdown that has options for sanchez, ronaldo, and ozil.
var roster = [
"ozil",
"sanchez",
"ronaldo"
];
var reps = null;
var dropdown = null;
var scorerOption = "<option value='" + reps + "' class='scorerOption'>" + roster[reps] + "</option>";
function makeDropdown () {
dropdown = "<select class='scorer'>" + String(scorerOption).repeat(roster.length) + "</select>";
document.getElementById("rawr").innerHTML = dropdown;
}
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<p id="rawr"><button onclick="makeDropdown()">Select a player</button></p>
</body>
</html>
As you may notice, the and tags appear, but all have innerHTML's and values of "undefined". How can I change that so it displays the names sanchez, ronaldo, and ozil?
You'll need to loop through the array and for each element in the array, create and insert a new option.
You should also not use inline HTML event handling attributes (onclick), see here for why.
Lastly, it's generally better to create dynamic elements with the DOM API call of document.createElement(), rather than build up strings of HTML as the strings can become difficult to manage and the DOM API provides a clean object-oriented way to configure your newly created elements.
var roster = [
"ozil",
"sanchez",
"ronaldo"
];
// Work with your DOM elements in JavaScript, not HTML
var btn = document.getElementById("btn");
btn.addEventListener("click", makeDropdown);
function makeDropdown () {
// Dynamically generate a new <select> element as an object in memory
var list = document.createElement("select");
// Configure the CSS class for the element
list.classList.add("scorer");
// Loop over each of the array elements
roster.forEach(function(item){
// Dynamically create and configure an <option> for each
var opt = document.createElement("option");
opt.classList.add("scorerOption");
opt.textContent = item;
// Add the <option> to the <select>
list.appendChild(opt);
});
// Add the <select> to the document
document.getElementById("rawr").appendChild(list);
}
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<p id="rawr"><button id="btn">Select a player</button></p>
</body>
</html>

Get Xpath from angleSharp element

Is there any definite way to retrieve the xPath of a angleSharp IElement
I'm trying to pass an IElement to a javaScript function, so I need a way to convert the angleSharp element to a javaScript Dom element
function selectLevels(element, name, level){
document.querySelectorAll("*").forEach(e => {
if(e.isEqualNode(element)){
e.setAttribute('level', level);
e.setAttribute('title', name);
}
})
}
I want to call this javaScript function which is in the page by passing an element from the C# code bellow, but I get an angleSharp not found error from the page.
IElement element = mainDoc.QuerySelector("strong");
Page.ClientScript.RegisterStartupScript(this.GetType(), "SelectLevel", "selectLevels('" + element + "', '" + name + "', '" + level + "')", true);
If you have a HTML document with JavaScript code and want to call a (global) function in the JavaScript code from C# then the following example works for me with AngleSharp 0.15 and AngleSharp.Js 0.14:
static async Task Main()
{
var html = #"<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<script>
function selectLevels(element, name, level){
element.dataset.level = level;
element.dataset.title = name;
}
</script>
</head>
<body>
<h1>Test</h1>
<section data-level=1 data-title='section 1'>
<h2>Section test</h2>
</section>
</body>
</html>";
var jsService = new JsScriptingService();
var config = Configuration.Default.With(jsService);
var context = BrowsingContext.New(config);
var document = await context.OpenAsync(req => req.Content(html));
var selectLevels = jsService.GetOrCreateJint(document).GetValue("selectLevels");
var jsElement = JsValue.FromObject(jsService.GetOrCreateJint(document), document.QuerySelector("section"));
selectLevels.Invoke(jsElement, "2", "section 2");
Console.WriteLine(document.DocumentElement.OuterHtml);
}
So basically you get the function with e.g. jsService.GetOrCreateJint(document).GetValue("selectLevels"); and call it with its Invoke method, passing in string arguments for the simple types and the IElement converted with JsValue.FromObject e.g. JsValue.FromObject(jsService.GetOrCreateJint(document), document.QuerySelector("section")).

Making table with objects in array?

I'm a beginner in javascript and I have a little problem with my code. I found an exercise and i'm trying to do it. I have to write a function that will insert text from variable into table. I never met something like this. This variable looks like four objects in array. I want to show text in the table when I press a button. There are two buttons. When I press "Fizyka" button i should see:
Fizyka
Ola Kowal
Ela Nowak
and when I press "Chemia":
Chemia
Ala Goral
Ula Szpak
So this is my code. All i can edit is function show(study):
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="utf-8">
</head>
<body>
<button onclick="show('fizyka')">Fizyka</button>
<button onclick="show('chemia')">Chemia</button>
<div id="list"></div>
<script>
var student=[
{name:"Ola", second_name:"Kowal", study:"fizyka"},
{name:"Ela", second_name:"Nowak", study:"fizyka"},
{name:"Ala", second_name:"Goral", study:"chemia"},
{name:"Ula", second_name:"Szpak", study:"chemia"},
];
function show(study)
{
if (study==='fizyka')
{
document.getElementById("list").innerHTML = "<h2>student.kierunek</h2><ul><li>student.name + " " + student.second_name</li><li>student.name + " " + student.second_name</li></ul>";
}
if (study==='chemia')
{
document.getElementById("list").innerHTML = "<h2>student.kierunek</h2><ul><li>student.name + " " + student.second_name</li><li>student.name + " " + student.second_name</li></ul>";
}
}
</script>
</body>
</html>
It's not working. I don't know how to insert text from this variable into table.
There is several problem with your code. I have written piece of code which is working and you can use it and inspire.
<button onclick="show('fizyka')">Fizyka</button>
<button onclick="show('chemia')">Chemia</button>
<div id="list"><h2></h2><ul></ul></div>
<script>
//Student array
var student=[
{name:"Ola", second_name:"Kowal", study:"fizyka"},
{name:"Ela", second_name:"Nowak", study:"fizyka"},
{name:"Ala", second_name:"Goral", study:"chemia"},
{name:"Ula", second_name:"Szpak", study:"chemia"},
];
function show(study)
{
console.log('ENTER show('+study+')');
//Select h2 element
var header = document.getElementById("list").firstChild;
//Set h2 element text
header.innerHTML = study;
//Select ul element
var list = document.getElementById("list").lastChild;
//Set inner html to empty string to clear the content
list.innerHTML = "";
//loop through students and set the appropriate html element values
for(var i = 0; i < student.length; i++){
//check whether student[i] studies study which is put as a paramter into the function
if(student[i].study === study){
//Create new li element
var li = document.createElement('li');
//Into li element add a new text node which contains all data about the student
li.appendChild(document.createTextNode(student[i].name + ' ' + student[i].second_name));
//add li element into ul
list.appendChild(li);
}
}
console.log('LEAVE show('+study+')');
}
</script>

Need help calling a function with parameters in JavaScript

I am working on a college project which is basically an elaborate news summarizer. I need to dynamically generate a list of article headings upon clicking a topic.
I have been able to do that and assign the list Ids and Values dynamically using the for loop.
Each list is clickable and calls a parameterized function, with parameter as that lists value.
I am unsure how to define the function and syntax to make this a possibility.
I need to do this so that each heading when clicked can use AJAX to interact with a servlet and generate an article corresponding to the heading based on their values.
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
<script type="text/javascript">
function change() {
var i = 0;
var set;
document.getElementById('content').innerHTML = '';
for (i = 1; i < 6; i++) {
var div = document.getElementById('content');
div.innerHTML = div.innerHTML + '<a href="#" onClick="rem()">'
+ '<h2 id="theading'+i+'" value="'+i+'">HEADING ' + i
+ ' WILL GO HERE</h2></a><br>';
}
}
function rem(value) {
document.getElementById('content').innerHTML = value
+ '<h1>THE NEWS WILL GO HERE</h1>';
}
</script>
</head>
<style type="text/css">
* {
list-style: none;
}
a {
text-decoration: none;
color: inherit;
}
a:hover {
color: orange;
}
</style>
<body>
<h3>TOPIC</h3>
<div id="content"></div>
</body>
</html>
How do I write the code so that rem() function can be called with its parameter as the value of its corresponding list.
I would completely rewrite your first function:
function change() {
var i = 0;
var set;
var div = document.getElementById('content');
div.innerHTML = '';
for (i = 1; i < 6; i++) {
var a = document.createElement('a');
a.href = "#";
a.onclick = rem.bind(null, i); // Here is the magical part
a.innerHTML = '<h2 id="theading'+i+'">HEADING ' + i
+ ' WILL GO HERE</h2>';
div.appendChild(a);
div.appendChild(document.createElement('br'));
}
}
Embrace the power of this.
onClick="rem(this)"
This will pass a reference to the clicked element. From there you can then traverse the DOM, using parents and children, to locate other elements, without having to deal with messy IDs.
You need to pass that value to method rem().
I am not absolute sure which value you wan't to pass for it but changing:
onclick="rem()"
to:
onclick="rem(' + i + ')"
will call method rem with an value of i when the link is clicked.
After that you can and should discard value="'+i+' from your h2 tag. It is not valid HTML.
Cheers.

Why is addEventListener only executing my function but not creating a mouseover/mouseon event for it?

I have an assignment and I can't seem to figure out this one part:
I have written a Javascript function to preload/precache two images for EACH image tag, from the "img" folder based on the ID of img-html tag. This part works fine.
However the function to change the image on mouseover and mouseout using addEventListener does not seem to work and I just can't seem to figure out why! If I debug, I can see the images are being loaded fine and there is no error. However on mouseover it just doesn't seem to work!
HTML Page:
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="author" content="Nilay Panchal"/>
<meta name="description" content="Part 1 Assignment 3"/>
<title>HomePage</title>
<script src="js/breadcrumb.js"></script>
<script src="js/preloader.js"></script>
<script type="text/javascript">
function animal(name,scientificName,typeOfFood,sleepingPattern,size) {
this.name = {
realName:name,
scientificName:scientificName
};
this.typeOfFood = typeOfFood;
this.sleepingPattern = sleepingPattern;
this.size = size;
}
function makeNewAnimal(name, scientificName, typeOfFood,sleepingPattern,size) {
var animalClicked = new animal(name, scientificName, typeOfFood,sleepingPattern,size);
var finalStringObject = "{"+myOwnRecursiveToStringMethod(animalClicked,"")+"}";
document.getElementById("stringOfObject").value = finalStringObject;
var backToObject = parseStringAndConvertToObject(finalStringObject);
document.getElementById("jsonDisplay").value = backToObject;
}
function myOwnRecursiveToStringMethod(objectToParse, outputString) {
var first = 1;
for(nameOfProperty in objectToParse){
if(objectToParse[nameOfProperty] instanceof Object) {
outputString += nameOfProperty+":{";
outputString = myOwnRecursiveToStringMethod(objectToParse[nameOfProperty],outputString);
outputString += "},";
} else{
outputString = outputString + nameOfProperty +":'"+ objectToParse[nameOfProperty]+"',";
}
}
if(outputString.charAt(outputString.length-1) == ',') {
outputString = outputString.slice(0,-1);
}
return outputString;
}
function parseStringAndConvertToObject(finalString) {
eval("var backToObject = "+finalString);
return backToObject;
}
</script>
</head>
<body>
<h3>Navigation List:</h3>
<nav id="navigationBar" style="border-style: double ;border-width: 10">
<ul>
<li>Assignment 3 Root</li>
<li>All Animals
<ul>
<li>Insects.html</li>
<li>Birds-Aves
<ul>
<li>The Mighty Pelican</li>
</ul>
</li>
<li>The Tall Giraffe</li>
<li>The Hefty Rhinoceros</li>
</ul>
</li>
</ul>
</nav>
<div style="border-style:groove;padding-bottom: 10px;padding-left: 10px">
<h4><u>BREADCRUMB:</u></h4>
<div id="breadcrumb"></div>
</div>
<div style="text-align: center">
<h3><u>Please click on any of the images below and the JSON object of the animals data will be displayed in the text area below!</u></h3>
<img style="width:300px;height:300px" id='pelican' alt="Pelican"/>
<img style="width:500px;height:300px" id="rhino" alt="Rhinoceros"/>
<br>
<img style="width:500px;height:300px" id="giraffe" alt="Giraffe"/>
<img style="width:300px;height:300px" id='owl' alt="Owl"/>
</div>
<hr>
<h5>Animal Objects to String:</h5>
<textarea style="width:100%" id="stringOfObject"></textarea>
<h5>Animal Strings back to Objects</h5>
<textarea style="width:100%" id="jsonDisplay"></textarea>
</body>
<script type="text/javascript">
window.onload = precacheAndLoad();
makeBreadcrumb(location.pathname);
</script>
</html>
Javascript file:
/*
*
* Below I wrote a function to perform all the image operations and precaching too. To add a new image simply do the following:
* 1) Include, the JS file and add a new img tag on the HTML page. The ID of this img tag should be the name of the animal (eg. hippo).
* 2) Save two images named the same as the animal, with a suffix of 1 and 2 for default image and hover over image. (eg. hippo1.jpg and hippo2.jpg)
* The script will take care of the rest!
*/
var allImageVariables = new Array();
function precacheAndLoad() {
var allImages = document.getElementsByTagName("img");
for(imageCount=0; imageCount<allImages.length ; imageCount++) {
currentImageId = allImages[imageCount].id;
eval("var "+currentImageId+"1 = new Image(300,300)");
eval("var "+currentImageId+"2 = new Image(300,300)");
eval(currentImageId+"1.src = 'img/"+currentImageId+"1.jpg'");
eval(currentImageId+"2.src = 'img/"+currentImageId+"2.jpg'");
allImageVariables[imageCount] = eval(currentImageId+"1");
allImageVariables[imageCount+allImages.length] = eval(currentImageId+"2");
allImages[imageCount].src = eval(currentImageId+"1.src");
allImages[imageCount].addEventListener("mouseover", hoverImage(currentImageId,imageCount+allImages.length), false);
allImages[imageCount].addEventListener("mouseout", outImage(currentImageId,imageCount), false);
}
}
function hoverImage(currentImageId, arrayIndexOfImageVariable) {
eval("document.getElementById('"+currentImageId+"').src = allImageVariables[arrayIndexOfImageVariable].src");
}
function outImage(currentImageId, arrayIndexOfImageVariable) {
eval("document.getElementById('"+currentImageId+"').src = allImageVariables[arrayIndexOfImageVariable].src");
}
Because you're executing the function and passing the return result rather than passing a reference to the function when using addEventListener(). When you use () on the function name, it is executed immediately. To pass only a reference, you need to pass just the name of the function or if you need custom arguments like you have, then you need to create a shell function that you can pass.
Furthermore, you're trying to use for loop variables inside an event handler so you need to create a closure in order to preserve the value of those variables for the event handlers long after the for loop has completed:
Change this:
allImages[imageCount].addEventListener("mouseover", hoverImage(currentImageId,imageCount+allImages.length), false);
allImages[imageCount].addEventListener("mouseout", outImage(currentImageId,imageCount), false);
to this (line breaks added for readability):
(function(imageCount, currentImageId) {
allImages[imageCount].addEventListener("mouseover", function() {
hoverImage(currentImageId,imageCount+allImages.length);
}, false);
allImages[imageCount].addEventListener("mouseout", function() {
outImage(currentImageId,imageCount);
}, false);
})(imageCount, currentImageId);
Also, why in the world are you using eval() like you are. You can code without it and have much better code, better performing and, in some cases, safer.
For example, this:
eval("var "+currentImageId+"1 = new Image(300,300)");
eval("var "+currentImageId+"2 = new Image(300,300)");
eval(currentImageId+"1.src = 'img/"+currentImageId+"1.jpg'");
eval(currentImageId+"2.src = 'img/"+currentImageId+"2.jpg'");
Can be this:
var images = [];
images.push(new Image(300,300));
images.push(new Image(300,300));
images[0].src = 'img/'+currentImageId+'1.jpg');
images[1].src = 'img/'+currentImageId+'2.jpg');
Rather than declaring variables with eval(), you can use either an object or an array and put the dynamic variables into them and then reference them from there rather than by a straight variable name. This is a common way of creating dynamic variables.

Categories