I am making a tic-tac-toe game.
I would like to add individual onclick event to each div so I can add either an "X" or an "O" depending on the div I click. "O" will be added later.
JavaScript:
function createDivs() {
for (i = 0; i < 9; i++) {
var d = document.createElement("DIV");
document.body.appendChild(d);
//Stuck here -borrowed some of this from a similar topic-
d.onclick = function() {
return function() {
var p = document.createElement("P");
var x = document.createTextNode("X");
p.appendChild(x);
d.appendChild(p);
}
}(i);
//-------------------------------------------------
var ii = document.createAttribute("id");
ii.value = "D" + i;
d.setAttributeNode(ii);
var z = "D" + i;
if (i == 3 || i == 6) {
document.getElementById(z).style.clear = "left";
}
}
}
html: <body onload="createDivs()">
Pass d as argument and accept it as argument so that closure will remember it when click handler-function will be executed.
d is nothing but the element you are creating(var d = document.createElement("DIV");)
function createDivs() {
for (i = 0; i < 9; i++) {
var d = document.createElement("DIV");
d.style.float = 'left';
document.body.appendChild(d);
d.onclick = function(d) {
//----------------^^^ Accept d here
return function() {
var p = document.createElement("P");
var x = document.createTextNode("X");
p.appendChild(x);
d.appendChild(p);
d.onclick = function() {}; //Unset function after click event
}
}(d);
//^^ Pass d here
var ii = document.createAttribute("id");
ii.value = "D" + i;
d.setAttributeNode(ii);
var z = "D" + i;
if (i == 3 || i == 6) {
document.getElementById(z).style.clear = "left";
}
}
}
div {
width: 30px;
height: 30px;
border: 1px solid black;
}
<body onload="createDivs()">
Update: As suggested by Barmar in comments, value of this in the event-handler function will be the element on which event registered.
function createDivs() {
for (i = 0; i < 9; i++) {
var d = document.createElement("DIV");
d.style.float = 'left';
document.body.appendChild(d);
d.onclick = function() {
var p = document.createElement("P");
var x = document.createTextNode("X");
p.appendChild(x);
this.appendChild(p);
this.onclick = function() {}; //Unset function after click event
};
var ii = document.createAttribute("id");
ii.value = "D" + i;
d.setAttributeNode(ii);
var z = "D" + i;
if (i == 3 || i == 6) {
document.getElementById(z).style.clear = "left";
}
}
}
div {
width: 30px;
height: 30px;
border: 1px solid black;
}
<body onload="createDivs()">
Related
I wrote a bunch of JS code to add click listeners to the HTML buttons. I have some CSS that applies to the existing HTML. But the CSS doesn't appear to apply to the HTML generated by the JavaScript.
Link to JSfiddle
As you can see, line 1 has all the proper spacing. But any of the generated lines by clicking any of the three buttons lose the spacing.
Here's the CSS:
.form-block {
padding: 5px;
margin: 5px;
border: 1px solid #000;
span {
padding-right: 15px;
}
}
Thanks for the help in advance! :)
What you see are actual spaces between the elements in your original HTML/row, and then a lack of spaces between the elements in your JS generated elements.
I would either remove the spaces between the elements in your HTML, and use a left/right margin value to create that space instead, or add a space or carriage return/new line between the elements when you add them in your JS.
I removed the spaces (by inserting HTML comments) in your original HTML, and added margin-right: 1em to the input and buttons in your element row.
/*This function interprets the line position.
//input is a lpos string which is in the form of x.x.x.x
//splits it by the '.'
//returns the array with position
*/
function lposToAr(lpos) {
var ar = lpos.split('.');
for (var i = 0; i < ar.length; i++) {
ar[i] = parseInt(ar[i], 10);
}
return ar;
}
//This function turns the position array back into a string
function lposToStr(posAr) {
return posAr.join('.');
}
//This function creates a new line after clicking the NL button.
function NL(lpos, e) {
e.preventDefault();
var cLine = document.getElementsByClassName('line')[0].cloneNode(true);
var cNL = document.getElementsByClassName('new-line')[0].cloneNode(true);
var cNN = document.getElementsByClassName('new-nested')[0].cloneNode(true);
var cI = document.getElementsByClassName('input')[0].cloneNode(true);
cI.value = '';
var cNI = document.getElementsByClassName('new-input')[0].cloneNode(true);
//figure out which position
var posAr = lposToAr(lpos);
var clickNode = document.getElementsByTagName('form')[0];
for (var i = 0; i < posAr.length; i++) {
clickNode = clickNode.children;
var skipCount = 0;
for (var j = 0; j < clickNode.length - 1; j++) {
if (clickNode[j].tagName != 'DIV') {
skipCount++;
}
}
clickNode = clickNode[posAr[i] + skipCount - 1];
}
//set the last element of the posAr the total number of same level <div>+1
var siblingDivCount = 0;
for (var i = 0; i < clickNode.parentNode.children.length; i++) {
if (clickNode.parentNode.children[i].tagName == 'DIV') {
siblingDivCount++;
}
}
var newPosAr = posAr;
newPosAr[newPosAr.length - 1] = siblingDivCount + 1;
//Update the cloned items with the new line info
var newPosStr = lposToStr(newPosAr);
cLine.innerHTML = newPosStr;
cNL.addEventListener('click', function(event) {
NL(newPosStr, event);
});
cNN.addEventListener('click', function(event) {
NN(newPosStr, event);
});
//cI doesn't need to change, unless there's a bug
cNI.addEventListener('click', function(event) {
NI(newPosStr, event);
});
//Make the new Div
var newDiv = document.createElement('div');
newDiv.setAttribute('class', 'form-block');
newDiv.appendChild(cLine);
newDiv.appendChild(cNL);
newDiv.appendChild(cNN);
newDiv.appendChild(cI);
newDiv.appendChild(cNI);
//Add the new Div
clickNode.parentNode.appendChild(newDiv);
}
function NN(lpos, e) {
e.preventDefault();
var cLine = document.getElementsByClassName('line')[0].cloneNode(true);
var cNL = document.getElementsByClassName('new-line')[0].cloneNode(true);
var cNN = document.getElementsByClassName('new-nested')[0].cloneNode(true);
var cI = document.getElementsByClassName('input')[0].cloneNode(true);
cI.value = '';
var cNI = document.getElementsByClassName('new-input')[0].cloneNode(true);
var posAr = lposToAr(lpos);
var clickNode = document.getElementsByTagName('form')[0];
for (var i = 0; i < posAr.length; i++) {
clickNode = clickNode.children;
var skipCount = 0;
for (var j = 0; j < clickNode.length - 1; j++) {
if (clickNode[j].tagName != 'DIV') {
skipCount++;
}
}
clickNode = clickNode[posAr[i] + skipCount - 1];
}
var childDivCount = 0;
for (var k = 0; k < clickNode.children.length; k++) {
if (clickNode.children[k].tagName == 'DIV') {
childDivCount++;
}
}
posAr.push(childDivCount + 1);
//Update the cloned items with the new line info
var newPosStr = lposToStr(posAr);
cLine.innerHTML = newPosStr;
cNL.addEventListener('click', function(event) {
NL(newPosStr, event);
});
cNN.addEventListener('click', function(event) {
NN(newPosStr, event);
});
//cI doesn't need to change, unless there's a bug
cNI.addEventListener('click', function(event) {
NI(newPosStr, event);
});
//Make the new Div
var newDiv = document.createElement('div');
newDiv.className = 'form-block';
newDiv.appendChild(cLine);
newDiv.appendChild(cNL);
newDiv.appendChild(cNN);
newDiv.appendChild(cI);
newDiv.appendChild(cNI);
//Add the new Div
clickNode.appendChild(newDiv);
}
function NI(lpos, event) {
event.preventDefault();
//copy text box
var cI = document.getElementsByClassName('input')[0].cloneNode(true);
cI.value = '';
//find the node that has been clicked
var posAr = lposToAr(lpos);
var clickNode = document.getElementsByTagName('form')[0];
for (var i = 0; i < posAr.length; i++) {
clickNode = clickNode.children;
var skipCount = 0;
for (var j = 0; j < clickNode.length - 1; j++) {
if (clickNode[j].tagName != 'DIV') {
skipCount++;
}
}
clickNode = clickNode[posAr[i] + skipCount - 1];
}
//add the new textbox
clickNode.insertBefore(cI, clickNode.getElementsByClassName('new-input')[0]);
}
document.getElementsByClassName('new-line')[0].addEventListener('click', function(event) {
NL(document.getElementsByClassName('line')[0].innerHTML, event);
});
document.getElementsByClassName('new-nested')[0].addEventListener('click', function(event) {
NN(document.getElementsByClassName('line')[0].innerHTML, event);
});
document.getElementsByClassName('new-input')[0].addEventListener('click', function(event) {
NI(document.getElementsByClassName('line')[0].innerHTML, event);
});
/**
* Add styling to the form to make it look appealing
*/
.form-block {
padding: 5px;
margin: 5px;
border: 1px solid #000;
}
span {
padding-right: 15px;
}
.form-block button, .form-block input {
margin-right: 1em;
}
<!--
DO NOT CHANGE THE CONTENTS OF THIS BLOCK
-->
<form class='myform'>
<div class='form-block'>
<span class='line'>1</span><!--
--><button class='new-line'>New Line</button><!--
--><button class='new-nested'>New Nested Line</button><!--
--><input class='input' type='text' placeholder='Enter Value...'><!--
--><button class='new-input'>Add input</button>
</div>
</form>
I am trying to make a memory board game. I got the css part of the game done, but how it the JavaScript part suppose to work out. I tried using the codes below. When I click on the box, even if they are the same, the box won't disappear and when it's not the same number, the box doesn't turn back. Also, for my "gamebox", I want to add a picture to be the background. I couldn't get it to work. Can anyone help me. Thanks.
<html>
<style>
#gamebox
{
position: absolute;
top: 100px;
left: 100px;
background-image: url("https://www.google.com/url?sa=i&rct=j&q=&esrc=s&source=images&cd=&cad=rja&uact=8&ved=&url=http%3A%2F%2Fwww.pokemontimes.it%2Fhome%2F2014%2F10%2Fannunciato-il-pokemon-center-mega-tokyo-in-apertura-a-dicembre%2F%3F%3Drelated&psig=AFQjCNFGPAm9tU9MR4AZJKe1s6F90F8UFg&ust=1454720806721506");
}
div.box
{
position: absolute;
background-color: red;
top: -800px;
left: -800px;
width: 100px;
height: 100px;
text-align: center;
font-size: 30px;
}
div.box:hover
{
background-color: blue;
}
</style>
<div id=gamebox></div>
<div id=boxdiv class=box onclick="clickedBox(this)"></div>
<script>
var squaresize = 100;
var numsquares = 6;
var numClicked = 0;
var firstClicked;
var secondClicked;
var game = [];
for (var i = 0; i < numsquares; i++)
{
game[i] = [];
for (var j = 0; j < numsquares; j++)
{
game[i][j] = Math.floor(Math.random()*5);
makeBox(i, j);
}
}
function theSame(abox, bbox)
{
var boxParts = abox.id.split("-");
var i = boxParts[1];
var j = boxParts[2];
var boxaNum = game[i][j];
var boxParts = bbox.id.split("-");
i = boxParts[1];
j = boxParts[2];
var boxbNum = game[i][j];
return(boxaNum == boxbNum);
}
function nextTurn()
{
if (numClicked != 2)
return;
if (theSame(firstClicked, secondClicked))
{
deleteBox(firstClicked);
deleteBox(secondClicked);
}
else
{
hideBox(firstClicked);
hideBox(secondClicked);
}
numClicked = 0;
}
function hideBox(box)
{
box.innerHTML = "";
box.style.backgroundColor = "red";
}
function deleteBox(box)
{
//really delete the box
box.style.backgroundColor = "";
}
function showBox(box)
{
var boxParts = box.id.split("-");
var i = boxParts[1];
var j = boxParts[2];
box.innerHTML = game[i][j];
box.style.backgroundColor = "black";
box.style.color = "white";
}
function clickedBox(box)
{
showBox(box);
numClicked++;
if (numClicked == 1)
{
firstClicked = box;
return;
}
if (numClicked == 2)
{
secondClicked = box;
}
}
function makeBox(i, j)
{
var boxdiv = document.getElementById("boxdiv");
var newBox = boxdiv.cloneNode(true);
newBox.style.left = i * (squaresize + 5);
newBox.style.top = j * (squaresize + 5);
newBox.style.width = squaresize;
newBox.style.height = squaresize;
newBox.id = 'box-' + i + '-' + j;
var gamebox = document.getElementById("gamebox");
gamebox.appendChild(newBox);
}
</script>
</html>
I think you're not calling nextTurn() anywhere in your code, meaning theSame() is never called, so nothing gets compared to eachother.
Maybe try calling nextTurn() when the numClicked === 2 in the clickedBox() function.
I need to write a function which counts types of elements in the DOM
function setNumbers(){
var a = 0;
var b = 0;
var c = 0;
var d = 0;
var span = document.createElement('span');
span.style.background = 'black';
var anchors = document.getElementById('anchors');
var buttons = document.getElementById('buttons');
var text = document.getElementById('text');
var events = document.getElementById('events');
var elements = document.getElementsByTagName("*");
for(var i =0; i<elements.length; i++){
if (elements[i].nodeName === 'A') {
a++;
span.innerHTML = "anchors - " + a;
span.style.border = '1px solid yellow';
span.style.color = 'yellow';
anchors.appendChild(span);
}
if (elements[i].nodeName === 'TEXTAREA' || elements[i].nodeName === 'INPUT' ) {
b++;
span.innerHTML = "textfields - " + b;
span.style.border = '1px solid orange';
span.style.color = 'orange';
text.appendChild(span);
}
if (elements[i].nodeName === 'BUTTON') {
c++;
span.innerHTML = "buttons - " + c;
span.style.border = '1px solid green';
span.style.color = 'green';
buttons.appendChild(span);
}
if (elements[i].onclick) {
d++;
span.innerHTML = "events - " + d;
span.style.border = '1px solid pink';
span.style.color = 'pink';
events.appendChild(span);
}
}
}
setNumbers();
Here is the code. The function has to append the number of elements in relevant div, but appends only textarea elements. Can you please check my function and tell where I missed something? Thanks
Here is the fiddle
You use the same span variable across the scope, so it is override everytime. The last element in your HTML matching if is textarea, so it is the last type being counted.
Shortest fix: remove span and use this instead:
if (elements[i].nodeName === 'A') {
a++;
anchors.innerHTML = "anchors - " + a;
anchors.style.border = '1px solid yellow';
anchors.style.color = 'yellow';
}
PS: better idea would be to create a function counting elements of a specified kind (eg. setNumbers(type)), now you repeat yourself many times and use global selector * choosing all the items, even though you need only these matching a, textarea etc.
Try this out:- http://jsfiddle.net/acguvd4f/8/
JS:-
function setNumbers(){
var a = 0;
var b = 0;
var c = 0;
var d = 0;
var anchors = document.getElementById('anchors');
var buttons = document.getElementById('buttons');
var text = document.getElementById('text');
var events = document.getElementById('events');
var elements = document.getElementsByTagName("*");
for(var i =0; i<elements.length; i++){
var span = document.createElement('span');
span.style.background = 'black';
if (elements[i].nodeName === 'A') {
a++;
span.innerHTML = "anchors - " + a;
span.style.border = '1px solid yellow';
span.style.color = 'yellow';
anchors.appendChild(span);
}
if (elements[i].nodeName === 'TEXTAREA' || elements[i].nodeName === 'INPUT' ) {
b++;
span.innerHTML = "textfields - " + b;
span.style.border = '1px solid orange';
span.style.color = 'orange';
text.appendChild(span);
}
if (elements[i].nodeName === 'BUTTON') {
c++;
span.innerHTML = "buttons - " + c;
span.style.border = '1px solid green';
span.style.color = 'green';
buttons.appendChild(span);
}
if (elements[i].onclick) {
d++;
span.innerHTML = "events - " + d;
span.style.border = '1px solid pink';
span.style.color = 'pink';
events.appendChild(span);
}
}
}
setNumbers();
This js is to create "lyricLine" and showing strings on random position of the page.
While creating an object of oLyricLine and calling drawText, it supposed to display each string of array lyricLines one by one until last line is shown. When last line is time-out, the timer should stop and the created div.lyricline should be deleted.
While different objects are created, (in this case LyricGen1 and LyricGen2) their timer should be ticking simultaneously.
But currently only timer of LyricGen2 is working.
Code is attached below.
<script src="jquery-min.js"></script>
<div id="container">
<div class="lyricline" id="0">
demo
</div>
</div>
<style>
body{
background: #000;
color: #fff;
font-family: sans-serif;
}
#container{
position: relative;
width: 100%;
height: 100%;
}
.lyricline{
position: absolute;
text-align: center;
}
</style>
<script>
var idarray = [];
//Object oLyricLine
function oLyricLine(obj){
this.lyricLines = [];
this.textSize = 30;
this.toppx = -1;
this.leftpx = -1;
this.unixtime = -1;
this.widthpx = -1;
this.colorhex = "#fff";
this.obj = obj;
}
oLyricLine.prototype.drawText = function(){
if (this.toppx != -1 && this.leftpx != -1 && this.unixtime != -1 && this.widthpx != -1){
var transitionInTime = 10;
var transitionOutTime = 10;
var tickfactor = 5;
$("#"+this.unixtime).css({
"color": this.colorhex,
"left": this.leftpx,
"top": this.toppx,
"width": this.widthpx,
"font-size":this.textSize
}).attr({"class":"lyricline","id":this.unixtime}).text(this.lyricLines[0]);
var tickCount = 0;
lyricLinesCount = 0;
var lyricLinesTick = [];
for (var i =0; i <= this.lyricLines.length - 1; i++) {
lyricLinesTick[i] = this.lyricLines[i].length * tickfactor;
if(i>0){lyricLinesTick[i]+=lyricLinesTick[i-1];}
};
var nLyricLines = this.lyricLines;
var nUnixtime = this.unixtime;
idarray[nUnixtime] = setInterval(function () {
tickCount += 1;
if (tickCount == lyricLinesTick[lyricLinesCount]){
lyricLinesCount +=1;
if(lyricLinesCount != lyricLinesTick.length){
$("#"+nUnixtime).text(nLyricLines[lyricLinesCount]);
}else{
$("#"+nUnixtime).remove();
clearInterval(idarray[nUnixtime]);
}
}
},100);
}
};
oLyricLine.prototype.widthGen = function() {
this.widthpx = maxWidth(this.lyricLines, this.textSize);
};
var unixtime1=Date.now();
$("#container").append($("<div></div>").attr("id",unixtime1));
var obj1=$("#"+unixtime1);
var LyricGen1 = new oLyricLine(obj1);
LyricGen1.lyricLines = ["gen1 line1","1.line2","1-------LINE#3"];
LyricGen1.textSize = 50;
LyricGen1.toppx = Math.floor(Math.random()*($(window).height()-LyricGen1.textSize));
LyricGen1.widthGen();
LyricGen1.leftpx = Math.floor(Math.random()*($(window).width()-parseInt(LyricGen1.widthpx)));
LyricGen1.unixtime = unixtime1;
LyricGen1.drawText();
$("#container").append($("<div></div>").attr("id","Datenow"));
var obj2=$("#Datenow");
var LyricGen2 = new oLyricLine(obj2);
LyricGen2.lyricLines = ["2.1","TWO=two","2........3","gen2 line number 4","2>>line5"];
LyricGen2.textSize = 80;
LyricGen2.toppx = Math.floor(Math.random()*($(window).height()-LyricGen1.textSize));
LyricGen2.widthGen();
LyricGen2.leftpx = Math.floor(Math.random()*($(window).width()-parseInt(LyricGen1.widthpx)));
LyricGen2.unixtime = "Datenow";
LyricGen2.drawText();
function strlen(str){ var len = 0; for (var i=0; i<str.length; i++) { var c = str.charCodeAt(i); if ((c >= 0x0001 && c <= 0x007e) || (0xff60<=c && c<=0xff9f)) { len++; } else { len+=2; } } return len; }
function maxWidth (lyricLines, textSize) {
var maxStringLength=0, maxStringId=-1;
for (var i = lyricLines.length - 1; i >= 0; i--) {
if (maxStringLength < strlen(lyricLines[i])){
maxStringLength = strlen(lyricLines[i]);
maxStringId = i;
};
};
$("#container").append($("<div></div>").css({
"background":"#fff",
"color":"#000",
"visibility":"hidden",
"font-size":textSize
}).attr({"class":"lyricline","id":"widgen"}).text(lyricLines[maxStringId]));
var maxPxLength = $("#widgen").css("width");
$("#widgen").remove();
return maxPxLength;
}
</script>
lyricLinesCount = 0;
You're missing a var here, making lyricLinesCount an implicit global variable. You want to have it local to each timer, though.
I have a function that addRow to a table
function addRow(tableId,rowEle)
{
var tab = document.getElementById(tableId);
var hrow = tab.insertRow();
var func = null;
for (var j = 0; j < rowEle.length; j++) {
var newTD = document.createElement("TD");
var elem = document.createElement(rowEle[j].desc);
elem.type = rowEle[j].type;
elem.type == "button" ? elem.value = rowEle[j].label : false;
rowEle[j].action != undefined ? elem.setAttribute(rowEle[j].action[0],rowEle[j].action[1]): false;
elem.style = rowEle[j].cssStyle;
newTD.appendChild(elem);
hrow.appendChild(newTD);
}
}
and my declaration looks like this
var eobject = new Object();
eobject.desc = "input";
eobject.type = "button";
eobject.label = "+";
eobject.action = new Array("onclick","addRow('tbl1',eleme)");
eobject.cssStyle = "width : 100%; border: 0px none; background-color:lightgreen;";
eleme.push(eobject);
** My Working Solution **
Thanks everyone for your help.