Using JavaScript to create and append nested div elements - javascript

I am trying to use JavaScript to update the CSS layout as the webpage loads. My code looks like so:
var container = 0; // Add Total UI
var containerTitle = 0; // Container Title
var article = 0;
var articleTitle = 0;
var divName = 0; // temp variable for article id names
var divNameT = 0; // temp variable for title id names
function setLayout(id) {
container = document.getElementById(id);
for(var x = 0; x < 18; ++x) {
// CREATE CONTAINER FOR ALL PANELS
divName = "articleCon"+ x;
article = document.createElement('div');
article.id = divName;
// SETUP CSS STYLE
article.style.cssText = 'height: 205px; width: 300px; background: red; margin-right: 20px; margin-bottom: 20px; display: block; float: left;';
setNewsTitle(count,divName); // Function Call to set Title Panel
container.appendChild(article);
}
}
function setNewsTitle(count,id) {
containerTitle = document.getElementById(id);
// CREATE CONTAINER FOR TITLE
divNameT = "articleTitle"+ count;
articleTitle = document.createElement('div');
articleTitle.id = divNameT;
// SETUP CSS STYLE
articleTitle.style.cssText = 'position: absolute; height: 45px; width: 100px; background: yellow; display: inline;';
containerTitle.appendChild(articleTitle);
}
When I compile my code without making the call to function setNewsTitle(count,id) all the CSS elements are working fine.
The issue I am facing here is whenever the function call is made, my page appears blank. Nothing displays on the screen.
I tried adding screenshots for better understanding, but i don't have the reputation yet.

Try ...
container.appendChild(article);
setNewsTitle(x,divName); // Function Call to set Title Panel
The article needs to be in place before setNewsTitle is run, since you are looking for the element by id. Also, you do not have count, you have x ...
jsFiddle: http://jsfiddle.net/rfornal/o1wyae74/

Try this, append child in DOM before call funtion setNewsTitle, replace count with x :
var container = 0; // Add Total UI
var containerTitle = 0; // Container Title
var article = 0;
var articleTitle = 0;
var divName = 0; // temp variable for article id names
var divNameT = 0; // temp variable for title id names
function setLayout(id) {
container = document.getElementById(id);
for(var x = 0; x < 18; ++x) {
// CREATE CONTAINER FOR ALL PANELS
divName = "articleCon"+ x;
article = document.createElement('div');
article.id = divName;
// SETUP CSS STYLE
article.style.cssText = 'height: 205px; width: 300px; background: red; margin-right: 20px; margin-bottom: 20px; display: block; float: left;';
container.appendChild(article);
setNewsTitle(x,divName); // Function Call to set Title Panel
}
}
function setNewsTitle(count,id) {
containerTitle = document.getElementById(id);
// CREATE CONTAINER FOR TITLE
divNameT = "articleTitle"+ count;
articleTitle = document.createElement('div');
articleTitle.id = divNameT;
// SETUP CSS STYLE
articleTitle.style.cssText = 'position: absolute; height: 45px; width: 100px; background: yellow; display: inline;';
containerTitle.appendChild(articleTitle);
}

You have 2 issues in your code:
You have not actually added the element to the DOM yet, so when you attempt document.getElementById in your function setNewsTitle() - it won't find anything.
You have an error in the method call to setNewsTitle(count,id). You are passing "count", but count doesn't exist. You need to call it as setNewsTitle(x, divName) but only AFTER you have made the call to container.appendChild(article).
The setLayout function would end up something like this:
function setLayout(id) {
container = document.getElementById(id);
for(var x = 0; x < 18; ++x) {
// CREATE CONTAINER FOR ALL PANELS
divName = "articleCon"+ x;
article = document.createElement('div');
article.id = divName;
// SETUP CSS STYLE
article.style.cssText = 'height: 205px; width: 300px; background: red; margin-right: 20px; margin-bottom: 20px; display: block; float: left;';
// Add it to the DOM first
container.appendChild(article);
// need to pass "X", not count
setNewsTitle(x,divName); // Function Call to set Title Panel
}
}

Related

Javascript: cannot access "style" property for div elements in array

I'm making a simple tic tac toe game as my introduction to JS and I ran into a problem almost immediately. I have a div with the class="container", and I use JS to create 9 more div elements inside it.
I have created the div elements with and put them in the cells[] array with no problem. The problem arrises when i try to access .style from the array elements.
const container = document.getElementById("container");
const cells = [];
for (let i = 0; i < 9;) {
cells[i] = document.createElement("div");
container.appendChild(cells[i]);
cells[i].onclick = function(){cells[i].style.backgroundColor = "red";} //this line is where the problem is
i++;
}
I have gone about this using addEventHandler() too, still with me not being able to access the .style property. When I type it in it doesn't show up on that autofill thing VSCode does.
Help?
Ps. I have noticed the cells[] array can't always access it's elements when inside a block.
This issue is due to how closures work in JavaScript.
Here's a demo with your current code (plus some CSS to make it clear what's happening):
const container = document.getElementById("container");
const cells = [];
for (let i = 0; i < 9;) { // actually, the problem is here...
cells[i] = document.createElement("div");
container.appendChild(cells[i]);
cells[i].onclick = function(){cells[i].style.backgroundColor = "red";} // ...here...
i++; // ...and here
}
div:not([class]) {
height: 20px;
border: 1px solid white;
background: cornflowerblue;
}
div:hover {
opacity: .5;
}
<container id="container"></container>
Note that the next div is always highlighted, not the one that was clicked.
Because you increment i within the block itself, that value is captured by the onclick callback, so it's always 1 higher than it should be.
Instead, you need to increment i within the parentheses as the third setup statement for the loop itself.
Here's the fix:
const container = document.getElementById("container");
const cells = [];
for (let i = 0; i < 9; ++i) { // increment here...
cells[i] = document.createElement("div");
container.appendChild(cells[i]);
cells[i].onclick = function() { cells[i].style.backgroundColor = "red"; }
// ...not here
}
div {
height: 20px;
border: 1px solid white;
background: cornflowerblue;
}
div:hover {
opacity: .5;
}
<container id="container"></container>
const container = document.querySelector('.container');
for (let i = 0; i < 9; ) {
const div = document.createElement('div');
container.appendChild(div);
div.addEventListener('click', chanegColor);
div.classList.add('setWidth');
i++;
}
function chanegColor() {
this.style.backgroundColor = 'red';
}
.setWidth {
width: 100px;
height: 100px;
border: 1px solid black;
}
<div class="container"></div>
const container = document.querySelector('.container');
for (let i = 0; i < 9; ) {
const div = document.createElement('div');
container.appendChild(div);
div.addEventListener('click', chanegColor);
div.classList.add('setWidth');
i++;
}
function chanegColor() {
this.style.backgroundColor = 'red';
}

Array JavaScript

I've started to make a memory game but I have a problem:
let card = [];
function makeCards() {
let cardSymbol = [];
const scorePan = document.querySelector('section');
scorePan.insertAdjacentHTML('afterend', '<ul class="deck"></ul>');
const cardDeck = document.querySelector('.deck');
for (let i = 1; i < 17; i++) {
card[i] = document.createElement('li');
card[i].setAttribute('class', 'card');
cardSymbol[i] = document.createElement('i');
cardSymbol[i].setAttribute('class', `fa fa-${symbols[i]}`);
card[i].appendChild(cardSymbol[i]);
cardDeck.appendChild(card[i]);
}
}
makeCards();
So int the array card I've stored all the cards, and added the cards in function makeCards , the problem is I want to add an Event Listener in another function to every single card, but if I iterate over the elements
with a for loop and I console.log() every element it shows me undefined, and this happens either I write the loop in a function or outside any function, but If I console.log(card) ; it shows me an array that contains all the elements of the array. Why I can't loop over them since I have an array?? Or should I add the eventListerns to elements when I create them in the function makeCards?????
You need a live event listener. If you are using jQuery you can use this
or if you are using native js, you can implement this
We can set one element that is a common ancestor to all card elements to listen for a "click" event occuring on it and/or its descendants. This is possible because of how an event bubbles. For details on this procedure read this article about Event Delegation.
The following Demo creates 2 arrays of matched FA classes and shuffles them. The ancestor element is created as deck and its descendant elements are card, and face elements which are created on each iteration of a for loop. Only the basic function of flipping is provided for each card/face, the logic for matching is beyond the scope of the OP question and requires a separate question to be posted.
Demo
Details are commented in Demo
/* Hardcode FA classes in symbol array
== Clone symbol array
== Use shuffle() function on both arrays
== Merge both arrays into faces array
*/
var symbol = ['fa-pied-piper-alt', 'fa-heart', 'fa-star', 'fa-star-o', 'fa-paw', 'fa-leaf', 'fa-cube', 'fa-cubes', 'fa-rebel', 'fa-empire'];
var match = symbol.slice(0);
var topCut = shuffle(symbol);
var bottomCut = shuffle(match);
var faces = topCut.concat(bottomCut);
// Create and reference parent element
var stack = document.createElement('main');
stack.id = 'deck';
// for each loop...
for (let c = 0; c < match.length * 2; c++) {
// Create a card element and add attributes
var card = document.createElement('a');
card.href = '#/';
card.classList.add.apply(card.classList, ['card', 'cover']);
/* Create a face element and add attributes
|| Note: each face is assigned a FA icon but not .fa class
*/
var face = document.createElement('i');
face.classList.add('face');
face.classList.add(faces[c]);
// Add each face to a card and each card to the deck (stack)
card.appendChild(face);
stack.appendChild(card);
}
// When complete, add deck to DOM
document.body.appendChild(stack);
// Reference deck
var deck = document.getElementById('deck');
/* Register deck to click event
|| When deck and/or its descendant elements are clicked...
|| invoke callback function flip()
*/
deck.addEventListener('click', flip, false);
// Pass Event Object
function flip(event) {
// Prevent <a> from jumping to a location
event.preventDefault();
/* event.target: origin of click event (clicked element)
|| event.currentTarget: element that listens for click (#deck)
|| if the clicked element is NOT #deck...
*/
if (event.target !== event.currentTarget) {
//...and if that clicked element is an <a>...
if (event.target.tagName === "A") {
/* toggle clicked <a> class .cover and
|| its child <i> class .fa on/off
*/
event.target.classList.toggle('cover');
var trump = event.target.firstElementChild;
trump.classList.toggle('fa');
}
}
}
// Utility function uses Fisher-Yates algorithm to shuffle arrays
function shuffle(array) {
var i = 0;
var j = 0;
var temp = null;
for (i = array.length - 1; i > 0; i -= 1) {
j = Math.floor(Math.random() * (i + 1))
temp = array[i]
array[i] = array[j]
array[j] = temp
}
return array;
}
#deck {
display: flex;
flex-flow: row wrap;
width: 450px;
height: 300px;
border-radius: 6px;
border: 5px inset brown;
background: green;
}
.card {
text-decoration: none;
display: inline-block;
width: 48px;
height: 64px;
border-radius: 6px;
border: 3px solid #000;
background: #fff;
font-size: 40px;
text-align: center;
margin: 5px
}
.face {
transform: translateY(11px);
pointer-events: none;
color: inherit;
}
.cover {
color: transparent;
background: #fff;
}
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">

Add a class iteratively with javascript

I'm creating a grid 16x16 with javascript. I also want the grid to do something when the mouse hover over each square. The problem is that I do not know how to add a class in a iterative way that every div get the same class.
Doing so, I would make jQuery do the action with one function applied to that class.
This is my present code
var contador = 1;
var outra = document.createElement('div');
outra.id = 'container';
document.body.appendChild(outra);
for (i=1;i<=16;i++){
for (j=1;j<=16;j++){
var divCreation = document.createElement('div');
var created = divCreation;
created.id = "numero"+ contador;
console.log(created.id);
created.textContent = ". ";
contador = contador + 1;
outra.appendChild(created);
}
}
$('#numero'+contador).hover(function(){
$(this).css('background-color','yellow');
});
If you want to give a specific class to your dynamically created elements, you can do it using className property.
Your code would be like this:
divCreation.className = "someClass";
Note:
I don't see why you are setting divCreation in a new variable, it's
just useless.
And instead of detecting hover with jQuery, you can just do it with css, look at my Demo below, or if you want to keep jquery you can use the class selector like this $('.box').hover(...).
Demo:
var outra = document.createElement('div');
outra.id = 'container';
var contador = 1;
document.body.appendChild(outra);
for (i = 1; i <= 16; i++) {
for (j = 1; j <= 16; j++) {
var created = document.createElement('div');
created.className = "box";
created.id = "numero" + contador;
created.textContent = ". ";
contador++;
outra.appendChild(created);
}
}
.box:hover {
background-color: yellow;
}
No need for use of ID for this at all...just use a common class. Also since using jQuery can create all this with a lot less code using by using it.
var $outra = $('<div>', { id: 'container'});
for (i = 1; i <= 16; i++) {
for (j = 1; j <= 16; j++) {
$outra.append( $('<div>', { class: 'box', text: '.'}) );
}
}
$('body').append($outra);
$('.box').hover(function() {
$(this).toggleClass('yellow');
});
.box {
width: 50px;
height: 50px;
display: inline-block;
border: 1px solid #ccc
}
.box.yellow {
background-color: yellow
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
You can use the attribute starts with selector
$('[id^=numero').hover(
or provide a class to the element and target the elements using the class
This will select all the elements whose id starts with the value you are specifying and attach a hover event.
And when you are using jQuery, why do you want to mix in both Vanilla JS and jQuery. You can just stick with one.
var contador = 1;
var $outra = $('<div/>', {
id: 'container',
class: 'container'
});
$('body').append($outra);
for (i=1;i<=16;i++){
for (j=1;j<=16;j++) {
var $divCreation = $('<div/>', {
id: 'numero' + contador,
text: '. ',
class: 'box'
});
$outra.append($divCreation);
contador = contador + 1;
}
}
$('[id^=numero').hover(function(){
$(this).css('background-color','yellow');
});
.container {
width: 200px;
height: 200px;
border: 1px solid green;
}
.box {
width: 20px;
height: 20px;
border: 1px solid black;
background: green;
float: left;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

JavaScript: creating an array of text boxes

I am looking to create a grid of 3x3 text input boxes, relative to an existing square div, using pure JavaScript. Preferably I would like to construct the grid of a single 1D array that cycles through every third box (if not, then an array of an array of input boxes would do - I hope this makes sense). This is what my code looks like at the moment, but only three of the boxes show when I cycle the array length (if I don't then the array extends linearly across beyond the div confines):
var row0 = new Array(9);
for (var i = 0; i < 9; ++i)
{
row0[i] = document.createElement('input');
row0[i].style.position = "absolute";
row0[i].type = "text";
row0[i].style.marginLeft = 35px *i % 105 + "px";
row0[i].style.width = "35px";
row0[i].style.height = "35px";
document.getElementById('block1').appendChild(row0[i]);
}
How can I get the grid to display correctly?
I would use a combination of javascript and CSS
DEMO http://jsfiddle.net/x8dSP/3010/
JS
window.onload = function () {
var parent_div = document.createElement("div")
parent_div.id = "parent"
document.body.appendChild(parent_div);
var x = 0;
while (x < 9) {
var child_input = document.createElement("input")
child_input.className = "child"
document.getElementById(parent_div.id).appendChild(child_input);
x++;
}
}
CSS
div {
width: 150px;
}
input {
display: inline-block;
width: 30px;
height: 30px;
margin: 5px;
}

JavaScript function not recognized

I am making a tic-tac-toe game in which I have a series of 9 buttons. I change the background image to an X or an O based on the boolean value of variable isX.
I have a div with an id stage which holds all the buttons of class square. I added a listener on the stage and passed it an event parameter. However, the function clickHandler is not recognized. Chrome says:
Uncaught ReferenceError: clickHandler is not defined 2Players.html:38
(anonymous function)
HTML:
<body>
<div id="stage">
</div>
</body>
CSS:
#stage{
width: 400px;
height: 400px;
padding: 0px;
position: relative;
}
.square{
width: 64px;
height: 64px;
background-color: gray;
position: absolute;
}
JavaScript:
const ROWS = 3;
const COLS = 3;
const GAP = 10;
const SIZE = 64;
var stage = document.querySelector("#stage");
var lotOfButtons = [];
var winningPatterns = ["123","159","147",
"258","357","369",
"456","789"];
var isX = true;
var player1Pattern = "";
var player2Pattern = "";
stage.addEventHandler("click",clickHandler,false);
prepareBoard();
function prepareBoard(){
for(var row = 0;row<ROWS;row++){
for(var col = 0;col < COLS;col++){
var square = document.createElement("button");
square.setAttribute("class","square");
stage.appendChild(square);
lotOfButtons.push(square);
square.style.left = col * (SIZE+GAP) + "px";
square.style.top = row * (SIZE+GAP) + "px";
}
}
function clickHandler(event){
if(isX===true){
event.target.style.backgroundImage = "url(../img/X.PNG)";
isX = false;
}else{
event.target.style.backgroundImage = "url(../img/O.PNG)";
isX = true;
}
}
}
You have some syntax and semantic errors in the code -- possibly more than I caught, but it is in a working state.
You only closed one of the braces in the nested for loops in prepareBoard. This leads to clickHandler being defined inside prepareBoard rather than on window.
It's addEventListener, not addEventHandler.
http://jsfiddle.net/4pgQ8/

Categories