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; }
Related
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.
I have two divs, one for left side and one for right side.
On the left one I appended 5 different images. I need to clone those 5 to the right one, append them to the right one and delete the last node.
For the left one:
var LS = document.getElementById("left");
var number = 5;
while(number > 0)
{
var theImage = document.createElement("img");
LS.appendChild(theImage);
number--;
}
For the right side:
var RS = document.getElementById("right");
var leftImages = LS.cloneNode(true);
RS.appendChild(leftImages);
while(RS.lastChild!=null)
{
var noviCh = RS.lastChild;
}
RS.removeChild(noviCh);
Code doesn't work. Anyone has idea why?
Instead of adding all the images and then removing one, how about just not adding the last one in the first place?
var LS = document.getElementById("left");
for (var i = 0; i < 5; i += 1) {
var theImage = document.createElement("img");
LS.appendChild(theImage);
}
var RS = document.getElementById("right");
var imgs = LS.getElementsByTagName('img');
for (var i = 0; i < imgs.length - 1; i += 1) {
RS.appendChild(imgs[i].cloneNode(true));
}
#left {
background-color: yellow;
}
#right {
background-color: green;
}
img {
width: 100px;
height: 100px;
background-color: red;
}
<div id="left"></div>
<div id="right"></div>
var LS = document.getElementById("left");
for ( var number = 5; number > 0; number--)
{
LS.appendChild(document.createElement("img"));
}
and
var RS = document.getElementById("right");
while( LS.lastChild )
{
RS.appendChild( LS.lastChild.cloneNode(true) );
LS.removeChild( LS.lastChild );
}
I want to change all div inside row be similar to the height of firstchild of row2 also if the classes are outside of row the div height will be auto and not be change. what's the problem in my code can anyone explain also I dont want to change it from css.
var lgh , lgh_in,mdh,mdh_in,smh,smh_in;
var r = ".row2 >";
var z = ['.lg','.lg-in','.md','.md-in','.sm','.sm-in',];
z.forEach(function (x){
var xx = document.querySelectorAll( r + x );
for ( var j = 0; j < xx.length; j++) {
var ch = document.getElementsByClassName('row2');
for(var i = 0 ; i < ch.length ; i++ ){
var ar = ['lg','lg-in','mdh','md-in','sm','sm-in']; //these are class names , if I put this into my div the div height should be change according to mdh ,lgh ,smh .... etc
for(var k = 0; k < ar.length;k++){
if(ch[i].firstChild.className === ar[k]){
lgh = ch[i].parentNode.clientHeight/ 1.5 ;
lgh_in = ch[i].parentNode.clientHeight - lgh ;
mdh = lgh/ 1.5 ;
mdh_in = ch[i].parentNode.clientHeight - mdh ;
smh = mdh/ 1.5 ;
smh_in = ch[i].parentNode.clientHeight - smh ;
var colors = {};
colors[ar[0]] = lgh;
colors[ar[1]] = lgh_in;
colors[ar[2]] = mdh;
colors[ar[3]] = mdh_in;
colors[ar[4]] = smh;
colors[ar[5]] = smh_in;
ch[i].firstChild.style.height = mdh + "px"; //if I remove the px then output is different also
document.getElementById('demo').innerHTML = mdh ; // only for output checking
if (ch[i].parentNode.tagName === "BODY") {
ch[i].style.height = "auto"; // if the div2 parent is body then height will be auto.
}
}
}
}
}
});
so I able to change the first child div but I cant change all div inside of the row2 which is my problem,
HTML
<div class="main"><div class="row2"><div class="lg">xfghjgjgx</div><div class="lg-in">xfgvcbvx</div></div>
<div class="row2"><div class="lg-in">xfghjgjgx</div><div class="lg">xfgvcbvx</div></div>
Jquery is easier.. Id do something like this?
// For each .box element
$('.box').each(function() {
// Set up the variables
var $this = $(this),
w = $this.find('img').width(), // Width of the image inside .box
h = $this.find('img').height(); // Height of the image inside .box
$this.width(w).height(h); // Set width and height of .box to match image
});
Variant with same height of all blocks ( https://jsfiddle.net/br3t/fmovbtp1/ )
var classes = ["lg", "lg-in", "md"];
var requiredHeight = window.getComputedStyle(document.querySelector(".row2 .lg")).height;
classes.forEach(function(cls) {
document.querySelectorAll("." + cls).forEach(function(item) {
item.style.height = requiredHeight;
});
});
I want to create div continuously and it should come on the next line if it reaches the outer div's width in JavaScript without using canvas and float.
for(i = 0; i < 20; i++) {
var ind = (Math.round(Math.random(0,2)*2));
var child = document.createElement("div");
child.setAttribute("id","div"+i);
child.style.backgroundColor=colors[ind];
maindiv.appendChild(child);
}
You can use display: inline-block; css property. Refer this
var maindiv = document.getElementById('parent');
for (var i = 0; i < 100; i++) {
var ind = (Math.round(Math.random(0, 2) * 2));
var child = document.createElement("div");
child.setAttribute("id", "div" + i);
child.setAttribute("class", "inline");
child.innerText = 'div';
//child.style.backgroundColor = colors[ind];
maindiv.appendChild(child);
}
.inline {
display: inline-block;
}
<div id="parent"></div>
So here is an example of the function I need to replicate:
document.getElementById('img1').onmouseover = function() {
document.getElementById('img1').style.width = expandTo + '%';
expandCompensate(1);
}
document.getElementById('img1').onmouseout = function() {
expandReset();
}
The situation is that I have a for loop creating some div elements, and the number of them is dynamic. As of right now, I have it creating 4 div elements, so I created 4 iterations of the above functions for img1, img2, img3 and img4. But what I would like to do is to have the onmouseover and onmouseout functions created dynamically based on how many div elements I've decided to create (based on a variable).
Is there any way to do this? Here is all the code for context (it's not much), there are comments in the JS with explanations for everything. The part I'm trying to automate is at the bottom:
https://jsfiddle.net/4w0714su/3/
And here is the working example for context of what I'm trying to achieve:
http://www.ericsartor.ca/imgwide
FYI: The image is I picked were random, I just needed high res images. Just doing this for practice! Thanks to anyone that can help me figure this out!
I can't understand your code very well, but I'll answer particularly what you're asking.
You can achieve what you want by doing a loop:
for (var i = 0; i < 4; i++) {
document.getElementById('img' + i).onmouseover = function() {
this.style.width = expandTo + '%';
expandCompensate(Number(this.id.replace('img', '')));
};
document.getElementById('img' + i).onmouseout = function() {
expandReset();
}
}
Note: you can't use the i variable inside the event handlers' functions, because it will always be 4, since it will finish the loop, and will never be changed again.
Another way of doing that is by using an IIFE (Immediately-invoked function expression), e.g:
for (var i = 0; i < 4; i++) {
(function(n) {
document.getElementById('img' + n).onmouseover = function() {
this.style.width = expandTo + '%';
expandCompensate(n);
};
document.getElementById('img' + n).onmouseout = function() {
expandReset();
}
})(i);
}
Doing that, you're passing to a function the current i value, so in that scope, the value of n will be a different one for each execution, e.g 0, 1, 2 and 3.
An immediately-invoked function expression (or IIFE, pronounced
"iffy") is a JavaScript design pattern which produces a lexical scope
using JavaScript's function scoping.
This could be achieved by iterating all those DOM elements and binding events in a loop.
As we bind events in loop, and event callback is being executed later when loop would be iterated completely, we need to maintaing the value of current iteration using CLOSURE.
Try this snippet:
var pageHeight = document.getElementById('findBottom').getBoundingClientRect().bottom,
numOfPics = 4; //the number of div elements to create
//creates the div elements within a container div in the HTML document
for (var i = 1; i <= numOfPics; i++) {
document.getElementById('imgContain').innerHTML += '<div id="img' + i + '" class="imgPane"></div>';
}
//used to resize all divs if the window changes size
window.onresize = function() {
pageHeight = document.getElementById('findBottom').getBoundingClientRect().bottom;
for (var i = 1; i <= imgClasses.length; i++) {
document.getElementById('img' + i).style.height = pageHeight + 'px';
}
for (var i = 1; i <= imgClasses.length; i++) {
document.getElementById('img' + i).style.width = 100 / imgClasses.length + '%';
}
};
//sets the height of each div to be the mximum height of the page (without scrolling)
for (var i = 1; i <= numOfPics; i++) {
document.getElementById('img' + i).style.height = pageHeight + 'px';
}
//makes all the divs equal percentage widths
for (var i = 1; i <= numOfPics; i++) {
document.getElementById('img' + i).style.width = 100 / numOfPics + '%';
}
//the percentage of the page the hovered image will expand to
var expandTo = 40;
//function for when an image is hovered over
function expandCompensate(whichImg) {
for (var i = 1; i <= numOfPics; i++) {
if (i != whichImg)
document.getElementById('img' + i).style.width = (100 - expandTo) / (numOfPics - 1) + '%';
}
}
//function for when the hovered image is left to reset the widths
function expandReset() {
for (var i = 1; i <= numOfPics; i++) {
document.getElementById('img' + i).style.width = 100 / numOfPics + '%';
}
}
(function bindEvents() {
for (var i = 1; i <= numOfPics; i++) {
document.getElementById('img' + i).onmouseover = (function(i) {
return function() {
document.getElementById('img' + i).style.width = expandTo + '%';
expandCompensate(i);
}
})(i);
document.getElementById('img' + i).onmouseout = function() {
expandReset();
};
}
})();
body,
p,
div {
margin: 0;
padding: 0;
}
body {} #findBottom {
position: absolute;
bottom: 0;
}
.imgPane {
float: left;
background-position: center;
transition: width 0.25s;
}
#img1 {
background-image: url('http://www.ericsartor.ca/imgwide/img//1.jpg');
}
#img2 {
background-image: url('http://www.ericsartor.ca/imgwide/img//2.jpg');
}
#img3 {
background-image: url('http://www.ericsartor.ca/imgwide/img//3.jpg');
}
#img4 {
background-image: url('http://www.ericsartor.ca/imgwide/img//4.jpg');
}
<div id="imgContain"></div>
<!-- ABSOLUTE ELEMENTS -->
<div id="findBottom"></div>
<!-- ABSOLUTE ELEMENTS -->