I'm using a function on my website to randomly addClass to a div.
works fine, but I can't find a way to not repeat the same class twice one after the other (cause sometimes the same class is added and we can think the code is not working)...
here is my jquery code :
$("#switch").click(function(){
var classes = ["vert", "escalier","reverse","demi_escalier","demi_escalier_2","ligne" ];
$("#logo").removeClass().addClass(classes[~~(Math.random()*classes.length)]);
});
can anybody help me with this ?
thanks
if you want classes not repeat you can use following:
var classes = ["vert", "escalier", "reverse", "demi_escalier", "demi_escalier_2", "ligne"];
var classesCopy = classes.slice();
$('#switch').click(function() {
if (!classesCopy.length) {
classesCopy = classes.slice();
} // once alls classes used up it starts from beginning
var classToAdd = classesCopy.splice(Math.floor(Math.random() * classesCopy.length), 1);
$('.current-class').text('current class: ' + classToAdd);
$('#logo').removeClass().addClass(classToAdd+'');
});
#logo {
width: 100px;
height: 100px;
background: green;
}
<div class='current-class'></div>
<div id='logo'></div>
<button id='switch'>switch</button>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
//put it in an IIFE so the variables are scoped down
(function(){
//constants, don't need to declare them in the click function over and over
var classes = ["vert", "escalier","reverse","demi_escalier"
,"demi_escalier_2","ligne" ];
//keep track of the last class used, -1 initial so no chance of mismatch
var lastNumber = -1;
var $logo = $("#logo");
$("#switch").on('click', function() {
//get a new index
var nextClass = Date.now() * 100 % classes.length;;
//while they match, keep getting a new one
while (nextClass === lastNumber) {
nextClass = Date.now() * 100 % classes.length;
}
$logo.removeClass().addClass(classes[nextClass]);
//save it off so we can do the double check again on the next execution
lastNumber = nextClass;
});
})();
Related
First post on here, so bear with me! I'm supposed to insert 100 h3 headings on page load ("Accusation 1, Accusation 2, Accusation 3,...Accusation 100"). We're only using 1 loop for the entire lab, and that will be used with other code in the lab, so I'm trying to do this without using a loop, if possible.
**Also, the lab is supposed to teach about scope and hoisting, so we can't use "let" or "const", only "var".
var accusation = 1;
var createHeading = function () {
var heading = $('<h3></h3>').text("Accusation " + accusation);
$('body').append(heading);
accusation++;
}
$(document).ready(function () {
createHeading();
accusation++;
console.log(accusation);
if (accusation > 100) {
return;
console.log('reached 100');
}
})
I'm wanting this function to repeat and increment without using a loop, but it's only producing the first h3 heading.
Recursion! Have the function call itself.
var accusation = 1;
var createHeading = function() {
var heading = $('<h3></h3>').text("Accusation " + accusation);
$('body').append(heading);
accusation++;
if (accusation >= 100) {
console.log("Reached 100;");
return;
} else {
createHeading();
}
}
$(document).ready(function() {
createHeading();
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
You could use Array.from() and it's internal mapper callback to build an elements array and simply append that array
var headings = Array.from({length:100}, (_,i) => $('<h3>', {text: `Accusation ${i+1}`}))
$('body').append(headings)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
I'm trying to do something which I thought was quite extremely simple, but not having much luck. I have two long lists of scores to compare, each pair sits in its own div. I'm on the lookout for a function which I could specify the div IDs, and have the different reflected in the third div. If the figure is positive, apply one class, and if negative, apply another.
<style>
.positive {
color: green;
}
.negative {
color: red;
}
</style>
<div id = "score">50</div>
<div id = "benchmark">30</div>
<div id = "diff"></div>
and in my javascript:
$(window).ready(function() {
$('#diff').html(diff);
});
var diff = calc("score", "benchmark");
function calc(divID1, divID2) {
div1 = document.getElementById(divID1);
metric = div1.innerHTML;
div2 = document.getElementById(divID2);
benchmark = div2.innerHTML;
c = Math.abs(a) - Math.abs(b);
// this is the difference here
return String(c);
};
I have D3 and JQuery loaded up. The numbers within the columns of divs are dynamically generated through other functions, so I can't hard code the styling.
You have some errors in your code. You can call calc function when the document is ready and handle the result there. I sum it up to this:
$(document).ready(function() {
//get the result of your calc function
var diff = calc("score", "benchmark");
//display the result and add class depend of the returning value
$('#diff').html(diff).attr("class", diff > 0 ? "positive" : "negative");
});
function calc(divID1, divID2) {
//get first number
var div1Num = parseInt($("#" + divID1).text(), 10);
//get second number
var div2Num = parseInt($("#" + divID2).text(), 10);
//make the calculation
var result = div1Num - div2Num;
//return the result
return result;
};
.positive {
color: green;
}
.negative {
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="score">50</div>
<div id="benchmark">30</div>
<div id="diff"></div>
Another example with negative result:
$(document).ready(function() {
//get the result of your calc function
var diff = calc("score", "benchmark");
//display the result and add class depend of the returning value
$('#diff').html(diff).attr("class", diff > 0 ? "positive" : "negative");
});
function calc(divID1, divID2) {
//get first number
var div1Num = parseInt($("#" + divID1).text(), 10);
//get second number
var div2Num = parseInt($("#" + divID2).text(), 10);
//make the calculation
var result = div1Num - div2Num;
//return the result
return result;
};
.positive {
color: green;
}
.negative {
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="score">10</div>
<div id="benchmark">30</div>
<div id="diff"></div>
Edit: You can replace parseInt with parseFloat according to your needs.
References
.attr()
You cannot calc() the diff between values of 2 elements when the DOM is not ready yet.
(Your current ready handler isn't performing the actual calculation but only appending the already miscalculated value to some element).
Try this instead:
$(document).ready(function() {
var diff = Math.abs($("#score").text()) - Math.abs($("#benchmark").text());
$('#diff').html(diff).addClass(diff > 0 ? 'positive' : 'negative');
});
Also, I changed your $(window).ready() to $(document).ready() instead.
You can use following statement in js to get expected output.
score = jQuery('#score').text();
benchmark = jQuery('#benchmark').text();
if(Math.abs(score) > Math.abs(benchmark))
{
jQuery('#diff').text('Positive');
jQuery('#diff').addClass('positive');
}
else
{
jQuery('#diff').text('Negative');
jQuery('#diff').addClass('negative');
}
You can check example on this link- http://jsfiddle.net/o3k6u99p/1/
I'm creating a script that takes two input dimensions, width, and height, and creates a scaled grid which is representative of how many blocks could fit in a box with the given dimensions with the following function:
function makeRow() {
for (var i = 1; i <= blocksTall; i++) {
var mb = document.createElement("div");
mb.setAttribute("class", "matrix-block mb-off");
mb.setAttribute("onClick", "select_mb('" + j + "," + i + "');");
placeBlocks.appendChild(mb);
if (i = blocksWide) {
placeBlocks.appendChild('br');
}
}
}
This function works fine to display the first row of blocks, and then inserts a break tag after the row has finished being rendered, which is exactly what I want to do. The problem is I need to generate 17 more rows, with the same number of blocks, each one under the previous row, so my first thought was, I'll just wrap another for loop around this first for loop and since there is a break there, it will render the new row below the previous one:
for (var j = 1; j <= blocksTall; j++) { // Vertical for loop.
for (var i = 1; i <= blocksWide; i++) { // Horizontal for loop.
var mb = document.createElement("div");
//mb.setAttribute("id", "matblock-" + i + "-" + j);
mb.setAttribute("class", "matrix-block mb-off");
mb.setAttribute("onClick", "select_mb('" + i + "," + j + "');");
placeBlocks.appendChild(mb);
}
if (j = blocksWide) {
placeBlocks.appendChild(brk);
}
}
Where blocksWide = 17. Here is a fiddle with the complete script. When I log the value for j in the console, it does in fact increment (which tells me that the for loop is working). What seems to be happening though is that it is for some reason rendering the row, and then either rendering the new row on top of it (seems unlikely since the break tag is rendered after each row completes) or, for some reason the children are destroyed each time a new "horizontal" for loop is run.
Does anybody know why this might be happening and how to properly get each row to be appended under the last row so it produces a grid of blocks instead of just one row?
Thanks in advance, any help is greatly appreciated.
So, I'm a bit confused about some aspects of your script, but I think you have two major issues.
Firstly, you only ever call document.createElement("br") once, which means you only ever create a single line-break; and a single line-break can only appear in one place in the DOM. This:
placeBlocks.appendChild(brk);
removes brk from its current position in the DOM and then puts it at the end of placeBlocks. You should change it to this:
placeBlocks.appendChild(document.createElement("br"));
Secondly, I don't think that if (j = blocksWide) { makes sense. Note that it's equivalent to this:
j = blocksWide;
if (blocksWide != 0) {
which means that it interferes with your for-loop by manipulating the value of j. I think the fix for that issue is simply to remove the whole if-check, and to perform its body unconditionally.
I really don't understand what you were trying to do with the remainder operators and the dividing, but blocksWide resolved to infinity causing an infinite loop, and blocksHigh was just 17. All of the other variables besides full weren't used.
You don't actually need two loops, although it is ok to do that. If you want to use just one loop you basically just need to know if i is a multiple of dispW.
So you divide i by dispW then you want to know if it is an integer, to find this you use the remainder operator for 1 and if it resolves to 0 it is an interger. It looks like this...
if ((i / dispW) % 1 === 0)
// if ( dispW=3 && ( i=3 || i=6 || i=9 || ... ) ) true;
This in a loop would look like
totalWidth = dispW * dispH; // total number of blocks
for (var i = 1; i <= totalWidth; i++) {
// do stuff;
if((i / dispW) % 1 === 0) {
// insert new line break;
}
}
The method you used for selecting the blocks was a round about way of doing it. First you shouldn't use inline javascript, second you shouldn't use javascript to embed inline javascript in a dynamically created element. Use element.onclick = function; instead.
Notice there is no braces after the function. This is because you are actually passing the function reference and not the returned value of the function.
element.onclick passes an event object to the function reference. You can use this to select the block that was clicked on like so.
for ( ... ) {
...
var element = document.createElement('div');
element.onclick = myFunction;
...
}
function myFunction(e) {
var clicked = e.target // this is the element that was clicked on
}
Also, you were creating one <br> element outside of the loop. Because appendChild moves elements and does not create elements it will just keep moving the line break until the loop finishes. It should look like this.
placeBox.appendChild(document.createElement('br'))
// append a newly created line break;
Then even if all the logic worked as intended and you create a new line break every time, floated blocks means no line breaks use display: inline-block; instead.
So in the end what you get is...
(Full difference)
window.onload = function () {
renderGrid();
};
function renderGrid() {
var blocksTall = document.getElementById('height-in').value;
var blocksWide = document.getElementById('width-in').value;
var blocksTotal = blocksWide * blocksTall;
var placeBlocks = document.getElementById('matrix-shell');
while (placeBlocks.firstChild) {
placeBlocks.firstChild.remove();
}
console.log(blocksWide + "/" + blocksTall);
for (var i = 1; i <= blocksTotal; i++) {
var mb = document.createElement("div");
mb.className = 'matrix-block mb-off';
mb.onclick = select_mb;
placeBlocks.appendChild(mb);
if (((i / blocksWide) % 1) === 0) {
var brk = document.createElement("br");
placeBlocks.appendChild(brk);
}
}
}
function select_mb(e) {
var cur_mb = e.target;
if (cur_mb.className == "matrix-block mb-off") {
// Turn cell on.
cur_mb.style.backgroundColor = "#00FF00";
cur_mb.className = "matrix-block mb-on";
} else {
//Turn cell off.
cur_mb.style.backgroundColor = "#000";
cur_mb.className = "matrix-block mb-off";
}
}
.matrix-block {
height: 10px;
width: 10px;
border: 1px solid #fff;
display: inline-block;
background-color: black;
}
.mb-off {
background-color: black;
}
#matrix-shell {
font-size: 0;
display: inline-block;
border: 1px solid red;
white-space: nowrap;}
<table>
<tr>
<td>Width:</td>
<td>
<input id="width-in" name="width-in" type="text" />
</td>
</tr>
<tr>
<td>Height:</td>
<td>
<input id="height-in" name="height-in" type="text" />
</td>
</tr>
<tr>
<td colspan="2">
<button onClick="renderGrid()">Compute</button>
</td>
</tr>
</table>
<br/>
<div id="matrix-shell"></div>
I am trying to play around with learning jQuery and have made the following jsFiddle:
http://jsfiddle.net/jkNK3/
The idea is to have a div's color change on click. Fine, I got that but I am wondering if there is a way to have the div's color change through multiple classes changes, perhaps with some sort of array or loop. Let me explain.
I have created several CSS classes like so:
.color1 {..}
.color2 {..}
.color3 {..}
.color4 {..}
.color5 {..}
.color6 {..}
and am wondering if we can do something like
addClass("color" + i)
where i can be looped through 1 - 6.
Is there any way to accomplish this? Thanks for the help.
This is a good place to consider the danger of global javascript namespaces. Here's a simple example that takes advantage of closures to avoid that with jquery:
$(function() {
var numb = 1;
// this bit of managing the color state swap is another topic for discussion, so keeping it simple
var colors_len = 6;
$("div").click(function() {
// this closure has access to the numb variable
if (numb < colors_len) {
numb++;
$(this).addClass("color" + numb);
$(this).removeClass("color" + (numb-1));
} else {
numb = 1;
$(this).removeClass("color" + colors_len);
$(this).addClass("color" + numb);
}
});
});
http://jsfiddle.net/2taH5/
ps. Jquery ui also has a swap class method but that is more for animations
In my opinion the easiest would be to just store the color number in jQuery's handy data(), and then increment it from that:
function fnClick() {
var numb = $(this).data('color') || 2;
$(this).addClass("color" + numb).data('color', ++numb)
}
FIDDLE
To make it go back to the first color after the last color etc
function fnClick() {
var numb = $(this).data('color') || 2;
numb = numb == 7 ? 1 : numb;
$(this).removeClass().addClass("color" + numb).data('color', ++numb)
}
FIDDLE
How about using a random number to give a random color to the div.
var classCount = 6;
$(document).ready(function () {
$("div").on("click", fnClick);
});
function fnClick(e) {
// Get the currently clicked element
var $this = $(e.target),
className = 'color' + Math.floor((Math.random() * classCount )+1);
// Remove the exixting class/s
$this.removeClass();
// Add the class
$this.addClass(className);
}
Check Fiddle
I'm having some problems, I'd like to have a sort of slideshow where users have 4 buttons, and when they click one div appears and the others disappear. The div's are all in the same place with the same size. I'd also like to put this automatic
var Idx = 1;
var IntervalKey = setInterval = (auto, 5000);
var auto = function() {
$("#MainImage").eq(Idx).fadeIn(1000);
while(Idx <3) {
Idx++;
$("#MainImage").eq(Idx).hide();
}
Idx++;
if(Idx>3) {
Idx = 0;
}
};
$(".botao-imagem").click(function(){
Idx = $(".botao-imagem").index(this);
auto();
});
Your main issue is repeated IDs, IDs must be unique, so $("#ID").eq() doesn't every have a purpose really, since it should be 1 or 0 results. First give the elements a class instead:
<div class="MainImage"><p>111111</p></div>
<div class="MainImage"><p>222222</p></div>
<div class="MainImage"><p>333333</p></div>
<div class="MainImage"><p>444444</p></div>
and use a class selector, like this:
$(".MainImage")
Also auto needs to be declared before using it or define it as a function directly, overall like this:
var Idx = 0;
var IntervalKey = setInterval(auto, 5000);
function auto() {
$(".MainImage").hide().eq(Idx).fadeIn(1000);
Idx++;
if(Idx>3) Idx = 0;
};
$(".botao-imagem").click(function(){
Idx = $(".botao-imagem").index(this);
auto();
});
You can test the updated/working version with the above code here.