8x8 div matrix onclick effectivization - javascript

I would like to have an 8x8 matrix where I can click on each square and the color will change, for use in a project.
I have made a 2d array and the entire 8x8 "board" but now I want to change to color on click, although the only way I can think of is through heavy code, for example writing div[row][column] 64 times...
var div = new Array(8);
for(var i = 0; i<8; i++){
div[i] = new Array(8)
}
for (var i = 0; i < 8; i++) {
for (var j = 0; j < 8; j++) {
div[i][j] = document.createElement("div");
div[i][j].style.width = "50px";
div[i][j].style.height = "50px";
div[i][j].style.backgroundColor = "white";
document.getElementById("container").appendChild(div[i][j]);
}
var jump = document.createElement("br");
document.getElementById("container").appendChild(jump);
}
div[0][0].onclick = function(){
if(div[0][0].style.backgroundColor == "white"){
div[0][0].style.backgroundColor = "red"
d00 = 1
}
else{div[0][0].style.backgroundColor = "white"
d00 = 0
}
}
I don't wish to write the above 64 times, surely there must be a better way.
#container {
margin: auto;
width:400px;
height:400px;
}
#container div {
display:inline-block;
vertical-align:top;
outline: 1px solid black
}

You can attach the onclick event to the parent container and use event.target to get the div which triggered the event:
document.getElementById("container").onclick = function(event){
if(event.target.style.backgroundColor == "white"){
event.target.style.backgroundColor = "red";
}
else{
event.target.style.backgroundColor = "white";
}
}

Indeed, there is a better way.
Inside the for-loop you're already setting important properties of your DIVs. It's the perfect place to attach an click eventListener whose callback function will handle the color switching.
Modify the for-loop like this:
var divElement;
for (var i = 0; i < 8; i++) {
for (var j = 0; j < 8; j++) {
divElement = document.createElement("div");
divElement.style.width = "50px";
divElement.style.height = "50px";
divElement.style.backgroundColor = "white";
divElement.addEventListener("click", changeColor);
document.getElementById("container").appendChild(divElement);
}
and use this function to actually change the color:
function changeColor(e) {
if (e.target.style.backgroundColor == "white") {
e.target.style.backgroundColor = "red"
d00 = 1
} else {
e.target.style.backgroundColor = "white"
d00 = 0
}
}
e.target refers to the object that triggered the event.
Forgive me, I don't know the purpose of d00.

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';
}

Access nesting elements

I use multiple buttons with a common class. When the user clicks on any of the buttons, I want to make elements with another class fill in red.
So basically I want to color everything inside .wrapper that has the .col class.
This is what I have so far.
var clickMe = document.querySelectorAll('.common');
for (var i = 0; i < clickMe.length; i++) {
clickMe[i].addEventListener('click', function (event) {
var x = document.querySelectorAll('#wrapper svg .col'); //this is where my issue starts.
x.style.fill = "red";
}, false);
}
Looking for a pure javascript solution.
Something like following should work for you:
var clickMe = document.querySelectorAll('.common');
for (var i = 0; i < clickMe.length; i++) {
clickMe[i].addEventListener('click', function (event) {
var x = document.querySelectorAll('#wrapper svg .col'); //this is where my issue starts.
for(var j=0;j<x.length;j++){
x[j].style.fill = "red";
}
}, false);
}

Style and change text content of children based on click

I am trying to style a single div and change the text content based on whether the user clicks on the item or not.
At the moment the code is extremely repetitive and I am looking for a method that will help me rewrite it to both work more efficiently and look cleaner.
Thank you.
let lvl0,
lvl1,
lvl2;
lvl0 = document.querySelector('.level-wrapper').children[0];
lvl1 = document.querySelector('.level-wrapper').children[1];
lvl2 = document.querySelector('.level-wrapper').children[2];
lvl0.addEventListener('click', changeStyle0);
lvl1.addEventListener('click', changeStyle1);
lvl2.addEventListener('click', changeStyle2);
function changeStyle0() {
document.querySelector('.text-header').textContent = levelTitle[0];
var showStyle = document.querySelector('.level-wrapper').children[0];
showStyle.style.opacity = '1';
showStyle.style.backgroundColor = '#95a5a6';
showStyle.style.border = '2px solid white';
showStyle.style.boxSizing = 'border-box';
console.log(showStyle);
}
function changeStyle1() {
document.querySelector('.text-header').textContent = levelTitle[1];
var showStyle = document.querySelector('.level-wrapper').children[1];
showStyle.style.opacity = '1';
showStyle.style.backgroundColor = '#95a5a6';
showStyle.style.border = '2px solid white';
showStyle.style.boxSizing = 'border-box';
console.log(showStyle);
}
function changeStyle2() {
document.querySelector('.text-header').textContent = levelTitle[2];
var showStyle = document.querySelector('.level-wrapper').children[2];
showStyle.style.opacity = '1';
showStyle.style.backgroundColor = '#95a5a6';
showStyle.style.border = '2px solid white';
showStyle.style.boxSizing = 'border-box';
console.log(showStyle);
}
var levelTitle = ["Question about Drinks/Soda/Water.", "Question about Portion Control/Meals.", "Question about Salt/Sugar."];
The method document.querySelector returns you only the first element in the document that fit the selector. To get an array of all the fits elements in the document, you have to use document.querySelectorAll method. For example:
const elements = document.querySelectorAll('.level-wrapper');
elemets[2].children[0].style = ...;
I think that this code is taking care of your issue:
const elements = document.querySelectorAll('.level-wrapper');
function showStyle(index, childIdx) {
elements[index].children[childIdx].style.opacity = '1';
elements[index].children[childIdx].style.backgroundColor = '#95a5a6';
elements[index].children[childIdx].style.border = '2px solid white';
elements[index].children[childIdx].style.boxSizing = 'border-box';
//console.log(levelStyle[index]);
}
function hideStyle(index, childIdx) {
elements[index].children[childIdx].style.opacity = '1';
elements[index].children[childIdx].style.backgroundColor = 'transparent';
elements[index].children[childIdx].style.border = 'none';
elements[index].children[childIdx].style.boxSizing = 'unset';
//console.log(levelStyle[index]);
}
/*for (let i = 0; i < elements.length; i++) {
for (let j = 0; j < elements[i].children.length; j++) {
showStyle(i,j);
}
}*/
for (let i = 0; i < elements.length; i++) {
for (let j = 0; j < elements[i].children.length; j++) {
(function (index) {
elements[i].children[j].onclick = function () {
elements[i].children[j].is_show = !elements[i].children[j].is_show;
if (elements[i].children[j].is_show) {
showStyle(i,j);
} else {
hideStyle(i,j);
}
}
})(j + i*elements[i].children.length);
}
}
<div class="level-wrapper">
<div>aaa</div>
<div>bbb</div>
<div>ccc</div>
<div>ddd</div>
</div>
<br><br>
<div class="level-wrapper">
<div>eee</div>
<div>fff</div>
<div>ggg</div>
<div>hhh</div>
</div>
This method ended up working for me.
Here is the Javascript:
function styleChange() {
const elements = document.getElementsByClassName("level");
for (let i = 0; i < elements.length; i++) {
elements[i].onclick = function () {
let el = elements[0];
while (el) {
if (el.tagName === "LI") {
el.classList.remove("active");
}
el = el.nextSibling;
}
this.classList.add("active");
};
}
}
styleChange();
Here is the html:
<ul class="level-wrapper">
<li class="level">
</a>1</li>
<li class="level">
</a>2</li>
<li class="level">
</a>3</li>
</ul>
And finally the css:
.active {
background-color: #95a5a6;
opacity: 1;
box-sizing: border-box;
border: 2px solid #fff;
}

the role of div in creating jquery objects

I'm trying to create a grid of circles which change color when clicked. The code I currently have to create the grid visually (which works) is this:
var color = null;
for (var r = 0; r < 5; r++) { // row
for (var c = 0; c < 5; c++) { // column
var myCircle = document.createElement('div');
myCircle.id = "circle";
myCircle.style.left = r * 56 + "px";
myCircle.style.top = c * 56 + "px";
document.getElementById('grid').appendChild(myCircle); //maybe error
}
}
I want to turn this into its jquery equivalent to handle the change in state on clicking. Here's what I've got so far:
var color = null;
for (var r = 0; r < 5; r++) { // row
for (var c = 0; c < 5; c++) { // column
var $myCircle = $("#circle");
$myCircle.style.left = r * 56 + "px";
$myCircle.style.top = c * 56 + "px";
$(document).ready(function () {
$($myCircle).click(function() {
$($myCircle).css('backgroundColor', 'color');
});
});
$('#grid').append($mycircle);
}
}
Any idea where I'm going wrong? Do I need to introduce the 'div' somewhere in the above code? How?
you are selecting an element and not creating here
$("#circle");
try this
$(document).ready(function () {
for (var r = 0; r < 5; r++) { // row
for (var c = 0; c < 5; c++) { // column
var $myCircle = $("<div />").attr({class:"circle"}).css({left:r * 56 + "px" ,top :c * 56 + "px"})); //creating a div element with its attributes...
$('#grid').append($mycircle); //appending it to grid
}
}
$('#grid').on('click','#circle',function() { //using on delegate for dynamically added div
$(this).css('backgroundColor', 'color');
});
});
for click event to work for dynamically added element, we need to delegate it to closest static parent. and ID should always be unique so its better to change your ID to class which i am doing here.
updated
updated some errors in your fiddle check it out..
remove the . in mycircle
var $myCircle = $('<div />').addClass('circle').css({
//-------------^---removed the `.`..
and changed all you javascript to jquery..
working fiddle
var $myCircle = $("#circle");
Should be:
var myCircle = $("#circle")[0];
Because you use the DOM native functions, but it's a jQuery wrapper.
Note that I removed the $ prefix from myCircle because now it's a DOM element.
When you're doing a bunch of appends like this, it performs better to use a document fragment. The code would look like this:
var frag = document.createDocumentFragment();
for (var r = 0; r < 5; r++) { // row
for (var c = 0; c < 5; c++) { // column
var $circle = $('<div/>').addClass('circle').css({left: r*56, top: c*56});
frag.appendChild($circle.get(0));
}
}
$('#grid').append(frag).on('click', '.circle', function(){
$(this).css('backgroundColor', 'red');
});
But I suggest going with an inline-block approach to layout your circles. The Compass mix-in is really handy for this. Also, the border-radius Compass mix-in. This way, your code is reduce to:
var frag = document.createDocumentFragment();
for (var i = 0; i < 25; i++)
frag.appendChild($('<div/>').addClass('circle').get(0));
$('#grid').append(frag).on('click', '.circle', function(){
$(this).css('backgroundColor', 'red');
});
Also, you don't need to specify + 'px' on jQuery css props. Don't forget the / before the > in $('<div/>') either. I created a jsFiddle here with the last solution.
And here's the supporting SCSS:
$radius: 50px;
$gap: 6px;
#grid {
width: ($radius + $gap) * 5;
}
.circle {
display: inline-block;
float: left;
border-radius: $radius;
width: $radius;
height: $radius;
border: $gap/2 solid white;
background-color: yellowgreen;
}
Even better, have the click do a toggleClass('active') and set the CSS .active {color: red; }

Getting the index of the current element and change his styles

I have a function whose destination is to work onClick event.
So, we have for example 4 Span elements and 4 Div elements.
The Spans are Tabs-buttons which I would like to "open" those Divs.
The 1st Span onClick would (open) change the style.display of the 1st Div in "block", from "none", and so on for the next Spans.
This piece of code works very well, but it changes only the design of elements.
function activateSup(s) {
var workTable = s.parentNode.parentNode.parentNode.parentNode.parentNode;
var spans = workTable.getElementsByTagName("span");
var supDivs = workTable.getElementsByClassName("supDiv");
for (var i = 0; i < spans.length; i++) {
spans[i].style.backgroundColor = "";
spans[i].style.border = "";
}
s.style.backgroundColor = "#5eac58";
s.style.border = "2px solid #336633";
}
I've tried to add the code below into my function to achieve what I want, but It does not work.
var getIndex = function(s) {
for (var index = 0; s != s.parentNode.childNodes[index]; index++);
return index;
}
for (var d = 0; d < supDivs.length; d++) {
if (getIndex == d) {
supDivs[d].style.display = "block";
}
else {
supDivs[d].style.display = "none";
}
}
I'm not exactly sure what you're trying to do, but one thing I noticed is this:
var getIndex = function(s) { /* .... */ }
for (var d = 0; d < supDivs.length; d++) {
if (getIndex == d) {
supDivs[d].style.display = "block";
}
else { /* ... */ }
}
This code is comparing getIndex to d, which means it's comparing an integer (d) to the function getIndex, instead of the result of the function call getIndex(spans[d]) (which is an integer, like d).
But what I think you're really trying to do, is getting the index of the clicked <span> so you can show the <div> with the matching index (and hide the rest). To achieve this, the code could be changed like so:
function activateSup(s) {
var workTable = s.parentNode.parentNode.parentNode.parentNode.parentNode;
var spans = workTable.getElementsByTagName("span");
var supDivs = workTable.getElementsByClassName("supDiv");
var index;
for (var i = 0; i < spans.length; i++) {
spans[i].style.backgroundColor = "";
spans[i].style.border = "";
if (s == spans[i])
index = i;
}
s.style.backgroundColor = "#5eac58";
s.style.border = "2px solid #336633";
for (var d = 0; d < supDivs.length; d++) {
if (index == d) {
supDivs[d].style.display = "block";
} else {
supDivs[d].style.display = "none";
}
}
}
Instead of the function getIndex, this just saves the correct index inside the first for loop.
There are many more improvements that could be made to this code, like rewriting it so you don't need that ugly s.parentNode.parentNode.parentNode.parentNode.parentNode and working with CSS classes instead of manually setting the style. But I'll leave that to the reader.

Categories