Attempting to replace an image in a table on a click - javascript

I am attempting to replace an image in a table on a click. I am not using JQuery; I am only Javascript to achieve this task. I think I have narrowed down the problem to the comment in the JavaScript code below. All other searches have ended me up with how to change image sources when they are part of the body etc. not part of a cell. I ran through other possible ways as well but for some reason I can't get it to work. Thanks so much!
HTML (Just cut out so you could see what each of the variables is referring to)
<td class="block"><img class="PieceImg" src="Directory/BlankSpace.png"></td>
JavaScript
var cells = document.getElementsByClassName("PieceImg");
for(var j = 0; j<cells.length;j++)
{
var cell = cells[j];
cell.addEventListener("click", function()
{
cell.src="Directory/RedPiece.png"; // This is the problem
alert('Test');
})
}

Assuming that you are running your JavaScript once the DOM is loaded, you should just have to replace cell.src with this.src.
var cells = document.getElementsByClassName("PieceImg");
for(var j = 0; j<cells.length;j++)
{
var cell = cells[j];
cell.addEventListener("click", function()
{
// Replace cell. with this.
this.src="Directory/RedPiece.png";
alert('Test');
})
}
The reason is simple: every time you loop, you create a variable called "cell" (var cell = cells[j];). This non-global variable is overwritten every time the loop is run. If you are running this code inside of a function, your cell variable will not even exist when it comes time to change the src. It will be undefined. Note that making this variable global would result in cell being the last element by class name PieceImg, so that wouldn't work any better.
However, if you use this instead, you will be referencing the "clicked" (remember addEventListener("click")) element when you click it, in this case your image. Thus you are able to change the src. Do a console.log of "this" inside your eventListener to see more things you can access. This gives a similar effect as adding the "click" handler inline:
<img src="fox.png" onclick="alert(this.src);" alt="Fox" />
Let me know if that makes sense to you.
Here's a fully function fiddle: https://jsfiddle.net/7se0vyf5/

Problem is the for loop and the reference of the variable. It does not keep the state when you assign the event handler.
var cells = document.getElementsByClassName("PieceImg");
for (var j = 0; j < cells.length; j++) {
var cell = cells[j];
(function(cell){
cell.addEventListener("click", function() {
console.log(cell);
cell.src = "Directory/RedPiece.png"; // This is the problem
alert('Test');
});
}(cell));
}
<table>
<tr>
<td class="block">
<img class="PieceImg" src="Directory/BlankSpace.png">
</td>
<td class="block">
<img class="PieceImg" src="Directory/BlankSpace.png">
</td>
<td class="block">
<img class="PieceImg" src="Directory/BlankSpace.png">
</td>
</tr>
</table>

Related

How to use a JavaScript loop to output an html div element 100 times?

How do I output my current div element 100 times using a loop in the function written below? The way I try to do it does not output anything except the div element a single time.
function printHundredtimes()
{
for(i = 0;i<100;i++)
{
document.getElementById("firstDiv").innerHTML += "<div id = "firstDiv"><center><p id ="paragraph">Hello, World </p></center></div>";
}
}
printHundredtimes();
<div id="firstDiv">
<center>
<p id="paragraph">Hello, World</p>
</center>
</div>
your code should be something like this
for (i = 0; i < 100; i++){
var div = document.createElement('p');
div.textContent = "Hello World";
div.setAttribute('class', 'paragraph');
document.getElementById("paragraph-container").appendChild(div);
}
Id should be unique for all elements, otherwise you should use "class" if you want to identify more than one element.
<div id="paragraph-container" style="text-align: center;">
<p class="paragraph">Hello World</p>
</div>
As Jon P said in the comment, center tag is obsolete. You can put text-align to the parent element of the paragraphs through inline css or internal css
The reason it's not printing anything is because of an error:
"<div id = "firstDiv"><center><p id ="paragraph">Hello, World </p></center></div>";
You're quotes are not escaped.
Consider changing it to:
"<div id = \"firstDiv\"><center><p id=\"paragraph\">Hello, World </p></center></div>";
Even better, consider multiplying the string then adding it to the array. as follows:
function repeat(count, str) {
return str.repeat
? str.repeat(count)
: (function () {var arr = []; for (var i = 0; i < count; ++i) {arr.push(str);} return arr.join('');})()
}
function printHundredtimes() {
var str = repeat(100, '<center><p id ="paragraph">Hello, World </p></center>'));
document.getElementById("firstDiv").innerHTML = str;
}
Note, I detected you were writing another id "firstDiv" inside the existing "firstDiv", which didn't seem to make sense to have two of them.
The above method does not tax the dom with repeated changes. So its better only to write to the dom once. Instead as above, you can multiply the string 100 times and then write it to the dom. Otherwise the browser would start to get really slow and freeze up eventually because of all the constant changes to it.
Also its better practice to break down your big functions into smaller ones. It makes them easier to reuse elsewhere. Let a functions name describe its single action. That's pure programming.

how to have div contain only the element from last (javascript) function call (if called multiple times)?

So I am creating a table through javascript,
Part of my assignment says: "the output div should only contain the table of the most recent call"
It is a class where we don't talk too much syntax and I am new to web development and javascript.
I have this function and it works:
<body>
<div id="output"></div>
<script type="text/javascript">
var functionCreate = function(strInput) {
var dividedArray = strInput.split("\n");
var dLength = dividedArray.length;
var myRow, myCell;
var myNewTable = document.createElement('table');
myNewTable.border = "1"
for(var i = 0; i< dLength; i++){
if(dividedArray[i].length >0){
myRow = myNewTable.insertRow(-1);//here
for(var j = 0; j<dividedArray[i].length;j++){
if(dividedArray[i][j] != ','){
myCell = myRow.insertCell(-1); //here
myCell.innerHTML = dividedArray[i][j]
}
}
}
}
document.getElementById("output").appendChild(myNewTable);
};
</script>
</body>
When its called one time, it does what it is supposed to do. When it is called twice, I get two tables, naturally(?)
However I have no idea how to only use the table from last call . I have no access to where it is being called. Can anyone direct me towards the right direction? What is the most basic and straight forward approach that I should take to implement this?
The reason why your code is adding new tables is due to the line of code here:
document.getElementById("output").appendChild(myNewTable);
The function appendChild appends (adds) a child (myNewTable) to the end of the element of id output.
So when u run the function multiple times, it just keeps adding a newly created myNewTable element to the output div.
To make sure it only appends the latest table, clear the output div with something like document.getElementById('output').innerHTML = ""; at the beginning of your function

remove onclick in a TD tag

I have a TD tag in my HTML as follows
<TD onclick="Javascript:OpenModal(....);.."></TD>
I need to write something in Javascript to remove the above onclick event dynamically
How do I do that? I tried following ways:
document.getElementsByTagName("TD")[x].onmousedown = null;
document.getElementsByTagName("TD")[x].removeAttribute = "onclick"
document.getElementsByTagName("TD")[x].removenamedAttribute("onclick")
I also tried disabling the whole table and it worked but I dont want to disable (because it becomes grey). Can someone please help me in this regard?
You're missing this
Syntax: element.removeAttribute(attrName);
document.getElementsByTagName("TD")[x].removeAttribute("onclick");
You may try like this:-
document.getElementsByTagName("TD")[x].removeAttribute("onclick");
removeAttribute("onclick") is somehow not working for me and i do not know why .I am totally new to these scripting languages. But some how I made it to work by disabling that particular single cell.. since it did not have any data other than onclik event it did not matter me by disabling it. Here is my psuedocode
 
var table = document.getElementById("table_id");
for (var i = 0, row; row = table.rows[i]; i++) {
for (var j = 0, col; col = row.cells[j]; j++) {
if (j == 7) { row.cells[j].disabled = true; }
}
}
 
i am trying to improve the above code to make it dynamic..let's see how it goes..

Javascript - Dynamically assign onclick event in the loop

I have very simple html page with js code:
<html>
<head>
<title></title>
</head>
<body>
<div id="divButtons">
</div>
<script type="text/javascript">
var arrOptions = new Array();
for (var i = 0; i < 10; i++) {
arrOptions[i] = "option" + i;
}
for (var i = 0; i < arrOptions.length; i++) {
var btnShow = document.createElement("input");
btnShow.setAttribute("type", "button");
btnShow.value = "Show Me Option";
var optionPar = arrOptions[i];
btnShow.onclick = function() {
showParam(optionPar);
}
document.getElementById('divButtons').appendChild(btnShow);
}
function showParam(value) {
alert(value);
}
</script>
</body>
</html>
That page binds 10 buttons, but when you click on any button it always shows alert "option9". How is it possible assign onclick event to show correspondent option !?
Thanks!
You'll have to do something like this:
btnShow.onclick = (function(opt) {
return function() {
showParam(opt);
};
})(arrOptions[i]);
Consider the fact that when the onclick() function is executed, all it has is:
showParam(optionPar);
, verbatim. The optionPar will be resolve at the time the click event is executed, and at this point it most likely be the latest value you assigned to it. You should generally avoid passing variables in such a way.
The problem you are trying to solve is best solved by re-writing the piece such as:
btnShow.value = "Show Me Option";
var optionPar = arrOptions[i];
btnShow.optionPar = optionPar;
btnShow.onclick = function(e) {
// if I'm not mistaking on how to reference the source of the event.
// and if it would work in all the browsers. But that's the idea.
showParam(e.source.optionPar);
}
The accepted answer seems to work, but seems to be confusing and a somewhat cumbersome way to do it. A better way perhaps might be to use the data attribute for the element you're looking to assign the event listener for. It's simple, easy to understand, and way less code. Here's an example:
btnShow.data = arrOptions[i];
btnShow.onclick = function() {
showParam(this.data);
}
I attach an event handler:
window.onload = function() {
var folderElement;
tagFolders = document.getElementById("folders");
for (i = 0; i < folders.length; i++) {
folderElement = folderButtons[i];
folderElement = document.createElement("button");
folderElement.setAttribute("id", folders[i]);
folderElement.setAttribute("type", "button");
folderElement.innerHTML = folders[i];
if (typeof window.addEventListener !== "undefined") {
folderElement.addEventListener("click", getFolderElement, false);
} else {
folderElement.attachEvent("onclick", getFolderElement);
}
tagFolders.appendChild(folderElement);
}
which can retrieve anything from the element that triggered the event:
// This function is the event handler for the folder buttons.
function getFolderElement(event) {
var eventElement = event.currentTarget;
updateFolderContent(eventElement.id);
}
in which case you have to embed the option inside the element / tag. In my case I use the id.
For jquery, check out the adding event data section from the API:
...
for (var i = 0; i < arrOptions.length; i++) {
$('<input id="btn" type="button" value="Show Me Option"><input>').appendTo("#divButtons")
$('#btn').bind("click", {
iCount: i},
function(event) {
showParam(arrOptions[iCount]);
});
}
The accepted answer is correct but I feel that no real explanation was done.
Let me try to explain, the issue here is classical missing closure.
The variable 'i' is getting increased by 1 per loop iteration,
and the on-click event actually is not being executed, whether only applied to the a element, it getting summarize up to the length of arrOptions which is 10.
So, the loop continues up until 'i' is 10,
Then, whenever the on-click event is being triggered, it takes the value of i which is 10.
now, for the solution,
in the solution we are using a closure, so that when we apply the value of 'i' to the on-click event of the a element, it actually gets the exact value of i at in time.
The inner function of the onclick event create a closure where it references the parameter (arrOptions[i]), meaning what the actual i variable is at the right time.
The function eventually closes with that value safely,
and can then return its corresponding value when the on-click event is being executed.
You pass just the reference of the variable to the function, not it's value. So every time the loop is iterated, it assigns a reference to your anonymous function and all of them point to the same value in memory. But since you use the same variable name in the loop, you overwrite the value of the variable. You can concatenate the variable to a string to preserve it's value. For example like that:
btnShow.onclick = new Function("", "showParam(" + arrOptions[i] + ");");
The first parameter is the name of the function but afaik it is optional (it can be left blank or omitted at all).
pp();
function pp()
{
for(j=0;j<=11;j++)
{
if(j%4==0)
{
html+= "<br>";
}
html += "<span class='remote' onclick='setLift(this)' >"+ j+"</span>";
}
document.getElementById('el').innerHTML = html;
}
function setLift(x)
{
alert(x.innerHTML);
}

Under loop, how to assign event depending on the condition based on index value

I have a loop of 50 divs, in which I am assigning event based on the current element. However I want to assign event based on a condition.
For example, divs are arranged one behind another, just like a photo stack.
I want user to first hit first div, then only they should able to hit second div then third div and so on till they reach the end of divs.
Because of the below code user can hit on any of the div and perform action. But action needs to be performed one by one. One div after another. Please suggest the solution.
var flag=0;
var flipImage=document.querySelectorAll('.shadow');
for(j=0; j<flipImage.length; j++){
var currentFlipImage=flipImage[j];
if(j==flag){
flag++;
currentFlipImage.addEventListener('click',flipDown,false);
}
}
Maybe something like:
var _handler = null;
var images = document.querySelectorAll('.shadow');
var length = images.length;
function attachHandler(index) {
if(_handler) {
// remove the click handler from the previous image
images[index-1].removeEventListener('click', _handler, false);
_handler = null;
}
if(index < length) {
var handler = function(event) {
flipDown(event);
attachHandler(index+1); // add the click handler to next image
}
images[index].addEventListener('click', handler, false);
_handler = handler; // capture reference for removal
}
}
attachHandler(0);
This only generates and adds event listeners when needed.
Important side note: The return value of querySelectorAll() is a NodeList which is live. That means any time you call length on this object, the list is reevaluated (the elements are searched again) which adds a performance overhead. Thus, never use list.length in the for loop. Capture the length beforehand.
Working DEMO
If you want to make it going in a loop, just remove the if statement (keep the body of course) and do
index = index % length;
Try giving each div a sequential ID then adding the click event to the next in sequence
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Test</title>
<script type="text/javascript">
function div_click(event)
{
var div_clicked = event.currentTarget,
next_sequence = parseInt(div_clicked.id.split("_")[1]) + 1,
next_div = document.getElementById("mydiv_" + next_sequence);
//alerts are just for testing here
alert(div_clicked.id);
alert(next_div.id);
div_clicked.removeEventListener('click',div_click,false);
next_div.addEventListener('click',div_click,false);
}
window.onload = function()
{
var first_div = document.getElementById("mydiv_0");
first_div.addEventListener('click',div_click,false);
};
</script>
</head>
<body>
<div id="mydiv_0">Click 0</div>
<div id="mydiv_1">Click 1</div>
<div id="mydiv_2">Click 2</div>
<div id="mydiv_3">Click 3</div>
<div id="mydiv_4">Click 4</div>
<div id="mydiv_5">Click 5</div>
</body>
</html>
Worth a shot?
Try this:
var flipImage = document.querySelectorAll('.shadow'), current = 0;
function flipDown(){
current++;
current = (current > flipImage.length)? 0 : current;
for(j = 0; j < flipImage.length; j++){
flipImage[j].style.zIndex = (j == current)? 2 : 1;
}
}
window.onload = function(){
for(j = 0; j < flipImage.length; j++){
flipImage[j].onmouseclick=flipDown;
}
}
When you have an arbitrary number of elements that share the same event handler, handling all the events from a common parent is usually better than assigning handlers to each element.
This doesnt work for 'focus' or non-bubbling events, but it is useful for touch, mouse and keyboard events.
It is usually easier to coordinate actions based on conditions from a constant 'switchboard' handler than from arbitrary elements.

Categories