This question already has an answer here:
Basic questions about javascript
(1 answer)
Closed 5 years ago.
I am working on a script that would generate random list of 100 elements where every third element would be clickable. So far I am stuck at stage below. Any ideas how to progress?
var hundred = Array(100);
hundred.toString();
for (i = 0; i < hundred.length; i++) {
document.write("Item " + (i + 1) + " of" + hundred.length + "</br>")
}
I used buttons. every third element will be clickable. remaining elements will have disabled property
var hundred = Array(100);
hundred.toString();
for (i = 0; i < hundred.length; i++) {
if(i%3===0 && i!==0){
var button = document.createElement("button");
button.innerHTML ="Click "+i ;
document.getElementsByTagName('body')[0].appendChild(button);
}else{
var button = document.createElement("button");
button.innerHTML ="Click "+i ;
button.disabled = true;
document.getElementsByTagName('body')[0].appendChild(button);
}
}
Edited: full example
var hundred = Array(100);
var node;
hundred.toString();
for (i = 0; i < hundred.length; i++) {
if(i%3===0 && i!==0){
node = document.createElement("button");
node.addEventListener('click', function() { alert('clicked'); });
node.innerHTML = 'clickablke';
} else {
node = document.createElement("div");
node.innerHTML = 'just div';
}
document.body.appendChild(node);
}
First you need create the element. Then apply the onclick with this consition i%3 == 0 to every 3 rd element
Updated
after click its a bolder using classList.add()
for (i = 1; i < 10; i++) {
var s = document.createElement('SPAN');
if (i % 3 == 0) {
s.className = 'clickable';
s.onclick = clicks;
}
s.textContent=i;
document.body.appendChild(s)
}
function clicks() {
console.log(this.innerHTML)
this.classList.add('bold')
}
.clickable {
color: red;
}
.bold{
font-weight:bolder;
}
As commented,
Instead of using document.write, use document.createElement to create an element and assign them event listener and append these elements to an element in html or document.body
var hundred = Array(100);
for (i = 0; i < hundred.length; i++) {
let el = document.createElement('span');
el.textContent = i + " ";
if((i+1) % 3 === 0){
el.classList.add('clickable')
el.addEventListener("click", notify)
}
document.body.appendChild(el)
}
function notify(){
this.classList.add('clicked')
console.log(this.textContent)
}
.clickable{
color: blue;
text-decoration: underline;
}
.clicked{
color: gray;
}
References
Why is document.write considered a "bad practice"?
Document.createElement
add onclick event to newly added element in javascript
Multiple wayst to do this, i'd make an event listener to every item reference, hence: every third clickable element goes bold:
var hundred = Array(100);
hundred.toString();
var btn = Array(100);
for (i = 0; i < hundred.length+1; i++) {
btn = document.createElement("p");
btn.innerHTML="Item " + (i-1 + 1) + " of" + hundred.length + "</br>";
if(i%3===0 && i!==0){
btn.addEventListener('click', function() {
this.style.fontWeight = 'bold'; }, false);
}
document.body.appendChild(btn);
}
Related
This question already has answers here:
How can I change an element's class with JavaScript?
(33 answers)
Closed 5 years ago.
I am working on a simple example, if a user clicks on element then all the elements above it should have a class and all elements below it should not have any class applied to them.
Here is my code:
<script>
function test(object) {
var pid = object.id;
var id = parseInt(pid.split("")[1]);
console.log(id);
for (var i = 1; i <= id; i++) {
var element = document.getElementById("p"+i);
console.log(element);
element.className = "active";
}
console.log(id+1);
for(var i = id+1; i <= 4; i++) {
var element = document.getElementById("p"+i);
element.className.replace(new RegExp('(?:^|\\s)'+ 'active' + '(?:\\s|$)'), ' ');
console.log(element);
}
}
</script>
<div id="divid">
<p id="p1" onclick="test(this)">one</p>
<p id="p2" onclick="test(this)">two</p>
<p id="p3" onclick="test(this)">three</p>
<p id="p4" onclick="test(this)">four</p>
</div>
So here if I click on three then the elements for one, two, three should have the class active and element four should not have any class. This is working fine.
Now if I click on one, I am expecting that two, three, four should have any css class but it is not working like that.
Can you please help me where is the issue. I want to use plain Javascript.
It might be wise to consider an alternative to using the onclick attribute due to separation of concerns. The following allows you to alter the HTML without having to consider JavaScript while you work.
https://jsfiddle.net/gseh0wxc/2/
var getList = (selector) => [].slice.call(document.querySelectorAll(selector));
var paragraphs = getList("#divid p[id ^= 'p']");
paragraphs.forEach((paragraph, index) => {
paragraph.addEventListener('click', (e) => {
for (let i = 0; i < index; i++) {
paragraphs[i].classList.remove('active');
}
for (let i = index; i < paragraphs.length; i++) {
paragraphs[i].classList.add('active');
}
});
})
Please try this code
function test(object) {
var pid = object.id;
var id = parseInt(pid.split("")[1]);
console.log(id);
for (var i = 1; i <= id; i++) {
var element = document.getElementById("p"+i);
element.classList.add("active");
}
console.log(id+1);
for(var i = id+1; i <= 4; i++) {
var element = document.getElementById("p"+i);
element.classList.remove("active");
}
}
Hope this helps.
try this simple approach instead, don't need to extract id number and all, and with a single simple loop.
function test(option) {
//this will select all p tags id starts with "p" inside div having id "divid" and return a array
var targetPTags = document.querySelectorAll("div#divid p[id^=p]")
var idx, flag=false;
//we are iterating over that array and taking each dom element in el
for(idx=0;idx<targetPTags.length;idx++) {
var el = targetPTags[idx];
if(flag) {
//do operation you want for after elements in el
} else if(option===el) {
flag=true; // we are making flag true when its the element that clicked and doing no operation
//do the operation you want for the element, may be the same as below operation in else
} else {
//do operation you want for before element in el
}
}
}
Kind of similar to "Chatterjee"'s solution, but here you go:
function test(object)
{
var parentElem = null;
var childElems = null;
var currElemSet = false;
var i=-1;
try
{
parentElem = object.parentElement;
if(parentElem!=null)
{
childElems=parentElem.getElementsByTagName(object.nodeName); // could refine to accommodate sibling nodes only
if(childElems!=null)
{
for(i=0;i<childElems.length; i++)
{
if(currElemSet) childElems[i].className = "";
else childElems[i].className = "active";
if(childElems[i]==object) currElemSet = true;
}
}
}
}
catch(e)
{
alert("Error: " + e.Message);
}
finally
{
}
}
I was actually using a script which allowed me to Show a div onclick and hide others but now I need to do the same with "class" instead of "id".
My current script:
function layout(divName){
var hiddenVal = document.getElementById("tempDivName");
if(hiddenVal.Value != undefined){
var oldDiv = document.getElementById(hiddenVal.Value);
oldDiv.style.display = 'none';
}
var tempDiv = document.getElementById(divName);
tempDiv.style.display = 'block';
hiddenVal.Value = document.getElementById(divName).getAttribute("class");}
What I tried using getElementsByClassName :
function layoutNEW(divName){
var hiddenVal = document.getElementById("tempDivName");
if(hiddenVal.Value != undefined){
var oldDiv = document.getElementById(hiddenVal.Value);
oldDiv.style.display = 'none';
}
var tempDiv = document.getElementsByClassName(divName);
for ( var i=0, len=tempDiv.length; i<len; ++i ){
tempDiv[i].style.display = 'block';
}
hiddenVal.Value = document.getElementById(divName).getAttribute("id");}
Any ideas ?
EDIT : A working example of my current script with "id" : JSFiddle
EDIT 2: It works great, but when the div (class) is cloned, only one of them is showing the div. Do you have an idea about this ? Where is a JSFiddle demonstrating the situation: JSFiddle
I think this is what you'd need. The idea is that you can use a data property on your <a> tags that will tell your click handler which classname to look for when showing an element. From there, you just hide the others. Here's a working demo:
var toggleControls = document.querySelectorAll("[data-trigger]");
var contentDivs = document.querySelectorAll(".toggle");
for (var i = 0; i < toggleControls.length; i++) {
toggleControls[i].addEventListener("click", function(event) {
var trigger = event.target;
var selector = "." + trigger.getAttribute("data-trigger");
var divToShow = document.querySelector(selector);
for (j = 0; j < contentDivs.length; j++) {
contentDivs[j].style.display = "none";
}
divToShow.style.display = "block";
});
}
.toggle {
height: 100px;
width: 100px;
display: none;
}
.div1 {
background-color: red;
}
.div2 {
background-color: blue;
}
.div3 {
background-color: purple;
}
.div4 {
background-color: green;
}
Show Div1
<br/>
Show Div2
<br/>
Show Div3
<br/>
Show Div4
<div class="toggle-container">
<div class="toggle div1"></div>
<div class="toggle div2"></div>
<div class="toggle div3"></div>
<div class="toggle div4"></div>
</div>
EDIT - As per updated question
In order to get this to work with dynamically created elements, you will have to put the var contentDivs = ... inside of the click handler, so you get a live version of that array. Also, you will need to change .querySelector to .querySelectorAll as the former only grabs the first matching element, not all as the latter does.
Here's what the code would look like: (note - I also moved the click handler into an outside function so it was not being recreated for every iteration of the loop, as is good practice)
function clickHandler(event) {
var contentDivs = document.getElementsByClassName("toggle"); // get live set of contentDivs in case any were added dynamically
var trigger = event.target;
var selector = "." + trigger.getAttribute("data-trigger");
var divsToShow = document.querySelectorAll(selector); // grab all matching divs
for (var i = 0; i < contentDivs.length; i++) {
contentDivs[i].style.display = "none";
}
for (var j = 0; j < divsToShow.length; j++) {
divsToShow[j].style.display = "block";
}
}
var toggleControls = document.querySelectorAll("[data-trigger]");
for (var i = 0; i < toggleControls.length; i++) {
toggleControls[i].addEventListener("click", clickHandler);
}
function cloneDiv() {
var elmnt = document.getElementsByClassName("container");
for ( var i=0; i<elmnt.length; i++ ) {
var cln = elmnt[i].cloneNode(true);
}
document.body.appendChild(cln);
document.getElementById("clone").appendChild(cln);
}
window.onload = cloneDiv();
Title, my only problem is that when I've created all elements on my page, and clicked all of them, my page looks like a chess board.
I can only "toggle" the background color of half too. So it's not only that they don't change color on the first click, they don't change at all.
This is my Javascript:
for (var i = 0; i < 10; i++) {
var itemContainer = document.createElement("div" + i);
itemContainer.id = "div" + i;
itemContainer.className = "item";
itemContainer.innerHTML = "Hello!";
for (let i = 0; i < 10; i++) {
$('div' + i).click(function() {
if (this.className == "item") {
this.className = "itemselected";
} else {
this.className = "item";
}
});
}
document.getElementById("page").appendChild(itemContainer);
}
I made a JSFiddle for you who want it.
I've seen a few other questions about how to toggle the color of backgrounds, but none of them have the same problem as me.
You inserted your second loop into the first one, every second i got skipped. And probably was able to change your divs up to i=18
JSFiddle
for (var i = 0; i < 10; i++) {
var itemContainer = document.createElement("div" + i);
itemContainer.id = "div" + i;
itemContainer.className = "item";
itemContainer.innerHTML = "Hello!";
document.getElementById("page").appendChild(itemContainer);
}
for (let i = 0; i < 10; i++) {
$('div' + i).click(function() {
if (this.className == "item") {
this.className = "itemselected";
} else {
this.className = "item";
}
});
}
Edit: You could simply put the content of your second loop into the first loop, to simplify your code a bit.
You don't need 2 loops try that
for (var i = 0; i < 10; i++) {
var itemContainer = document.createElement("div");
itemContainer.id = "div" + i;
itemContainer.className = "item";
itemContainer.innerHTML = "Hello!";
document.getElementById("page").appendChild(itemContainer);
$('#div' + i).click(function() {
alert("here");
if (this.className == "item") {
this.className = "itemselected";
} else {
this.className = "item";
}
});
}
fiddle example
You were close, missing "#" of id element
$('div' + i).click(function() {
$('#div' + i).click(function() {
and you have inserted the second loop inside first one
https://jsfiddle.net/snbtchph/
Your selector at line 8 of your JavaScript is missing the # so the jQuery is looking for <div0>, <div1>, <div2>..., and, your line 2 of JavaScript is var itemContainer = document.createElement("div" + i); which actual creating elements div0, div1....
And since you are using jQuery , I have also revised some code to use it instead of native JavaScript: https://jsfiddle.net/xfr496p6/5/
I have also added css .item { display: inline-block; } to makes the elements placed in a row.
There are a few problems with your code:
var itemContainer = document.createElement("div" + i);
Creating non-existant elements like <div1> is impossible, remove the iterator.
for (let i = 0; i < 10; i++) {
jQuery's .click() doesn't need a for loop, but adds the event listener to every case, this is not needed.
document.getElementById("page").appendChild(itemContainer);
Apply this directly in after the .innerHTML
In addition, you seem to randomly use ES6, jQuery, and VanillaJS through your entire codebase, I'd like to advise you to be consistant with how you write your applications.
I've updated your fiddle with the working changes.
https://jsfiddle.net/xfr496p6/8/
Updated javascript:
for (var i = 0; i < 10; i++) {
var itemContainer = document.createElement("div");
itemContainer.id = "div" + i;
itemContainer.className = "item";
itemContainer.innerHTML = "Hello!" + i;
document.getElementById("page").appendChild(itemContainer);
}
$('div').click(function() {
if (this.className == "item") {
this.className = "itemselected";
} else {
this.className = "item";
}
});
Why do you have 2 nested loops?
try this
for (var i = 0; i < 10; i++) {
var itemContainer = document.createElement("div" + i);
itemContainer.id = "div" + i;
itemContainer.className = "item";
itemContainer.innerHTML = "Hello!";
document.getElementById("page").appendChild(itemContainer);
}
for (let i = 0; i < 10; i++) {
$('div' + i).click(function() {
if (this.className == "item") {
this.className = "itemselected";
} else {
this.className = "item";
}
});
}
JSFIDDLE
for (var i = 0; i < 10; i++) {
var itemContainer = document.createElement("div" + i);
itemContainer.id = "div" + i;
itemContainer.className = "item";
itemContainer.innerHTML = "Hello!";
$(itemContainer).click(function() {
if (this.className == "item") {
this.className = "itemselected";
} else {
this.className = "item";
}
});
document.getElementById("page").appendChild(itemContainer);
}
I'm activating a javascript function with a Jquery onclick button:
$('#on').click(function() {
var a = document.getElementsByTagName('a');
for (i = 0; i < a.length; i++) {
a[i].addEventListener('click', function() {
var span = document.createElement('span');
var text = document.createTextNode(this.innerHTML + " ");
span.appendChild(text);
document.getElementsByClassName('output')[0].appendChild(span);
})
}
});
The problem is if the button is clicked more than once the function will repeat more than once. In this case it will print the output multiple times. How can I modify the javascript function to only print one character per click?
Example:
http://jsfiddle.net/874Ljaq1/
Use the jQuery event binding method one
$('#on').one("click", function() {
var a = document.getElementsByTagName('a');
for (i = 0; i < a.length; i++) {
a[i].addEventListener('click', function() {
var span = document.createElement('span');
var text = document.createTextNode(this.innerHTML + " ");
span.appendChild(text);
document.getElementsByClassName('output')[0].appendChild(span);
})
}
});
You can use the jQuery .data() function to set a flag when the button has been clicked once, and only proceed if the flag is not set.
The code:
$('#on').click(function () {
// if we have a flag that indicates this button has been clicked before,
// don't do anything.
if ($(this).data('clicked'))
return;
$(this).data('clicked', true); // set the flag
var a = document.getElementsByTagName('a');
for (i = 0; i < a.length; i++) {
a[i].addEventListener('click', function () {
var span = document.createElement('span');
var text = document.createTextNode(this.innerHTML + " ");
span.appendChild(text);
document.getElementsByClassName('output')[0].appendChild(span);
})
}
});
Please Look at the following code only the last image moves.
http://jsfiddle.net/u8Bg3/
But second one works
http://jsfiddle.net/u8Bg3/1/
As pointed by the Er144 even this works with jquery
http://jsfiddle.net/u8Bg3/14/
I also found out appendchild works but not innerhtml
The difference between two is that in first one html exits in second one it's dynamically created
HTML
<body>
<div class="racetrack" id="racetrack"></div>
<div id="track-tmpl" class="hide">
<div class="track"><div id="player{{ x }}" class="runner"></div></div>
</div>
</body>
JS
var position = [0,40,80,120,80],
racetrack = document.getElementById('racetrack');
track_tmpl = document.getElementById('track-tmpl').innerHTML;
function Players(ele, ptimeout)
{
this.el = ele;
this.i = 0;
this.iterations = 0;
this.stop = 0;
this.timeout = ptimeout;
this.position = 0;
this.animate = function(){
if(this.i !== 0){
this.move((this.position + 5), this.i);
}
if(!this.stop){
if(this.i < 5){
setTimeout(function(_this){
_this.i++;
_this.animate();
},this.timeout,this);
}
if(this.i==5){
this.iterations ++;
if(this.iterations < 50){
this.i = 0;
this.animate();
}
else{
this.el.style.backgroundPosition = '120px 0px';
}
}
}
};
this.start = function(){
this.stop = 0;
this.animate();
};
this.move = function(to,positionIndex){
this.position = to;
this.el.style.backgroundPosition = '-'+position[positionIndex]+'px 0px';
this.el.style.webkitTransform = 'translate('+to+'px)';
this.el.style.mozTransform = 'translate('+to+'px)';
}
}
function Game(noOfPlayers){
this.noOfPlayers = noOfPlayers;
this.players = new Array();
for (var i = 0; i < this.noOfPlayers ; i++){
racetrack.innerHTML = racetrack.innerHTML + track_tmpl.replace('{{ x }}', i);
this.players.push(new Players(document.getElementById('player' + i), (120 + i)));
/* issue here with dynamic added content*/
}
this.start = function(){
for (var i = 0; i < this.noOfPlayers; i++){
this.players[i].start();
}
};
}
var game = new Game(3);
game.start();
Why is that in dynamically added html only the last one moves
The issue is with creating the player(n) object inside the for loop along with the assignments to innerHTML using `+='. The modified fiddle: http://jsfiddle.net/u8Bg3/15/ works fine. Cheers for a good question!
var finalized_tracks= "" ;
for (var i = 0; i < this.noOfPlayers ; i++){
finalized_tracks += track_tmpl.replace('{{ x }}', i);
}
racetrack.innerHTML = racetrack.innerHTML + finalized_tracks;
for (var i = 0; i < this.noOfPlayers ; i++){
this.players.push(new Players(document.getElementById('player'+ i),(120+i)));
}
If you use the jquery:
var element = track_tmpl.replace('{{ x }}', i);
$(racetrack).append(element);
instead of the line where you change the innerHtml of racetrack div, all elements are moving.
However, I'm not sure, why...
theCoder has pretty much nailed the issue with your code there.
Just as an additional thought, you could manually build the necessary divs using javascript instead, it's more long winded however...
for (var i = 0; i < this.noOfPlayers ; i++){
var newTrack = document.createElement("div");
newTrack.id = "track"+i;
newTrack.className = "track";
var newPlayer = document.createElement("div");
newPlayer.id = "player"+i;
newPlayer.className = "runner";
newTrack.appendChild(newPlayer);
racetrack.appendChild(newTrack);
this.players.push(new Players(document.getElementById('player' + i), (120 + i)));
}