Assign random number to element in javascript but avoid duplicates - javascript

Please scroll down to bold text if you want to go straight to the question
I have made a page that consists of a grid of 9 tiles (divs).
Between 1-9 of those tiles could potentially have a slider inside it.
The sliders are all setup via a jQuery each function e.g
_gridSlider.each(function(){
// count slides, setup slider etc
}); // end slider each function
Everything works fine except the sliders all change at the same time and so I want to add some diversity into the start times.
Right now I create a random ID between 1 and X (X being the number of sliders) inside of the each function for each slider like so
_gridSlider.each(function(){
var _sliderID = Math.floor((Math.random() * _numSliders) + 1);
}); // end slider each function
I then start the sliders at a different time based around this ID like so
var _sliderStart = _sliderID + '000';
setTimeout(function() {
startTimer();
}, _sliderStart);
This works fine the only problem is that it is possible for 2 or more sliders to have the same ID, what I need is to assign each slider an ID between 1 and X but make sure that each slider has a different ID.
The end result would be have 1 timer starting at 1 second, another at 2 seconds, another at 3 seconds etc

You can use this function:
function generateId(numSliders) {
var store = generateId._store;
if (!store) {
generateId._store = {};
}
do {
var id = Math.floor(Math.random()*numSliders*1000+1);
} while (store[id])
store[id] = true;
return id;
}
Then you can use generated id as a start time:
var sliderId = generateId(slidersNumber);
var sliderStart = sliderId; // without + '000'
UPD generateId._store keeps used IDs inside itself. In that function store is used as a property of its function, to not add redundant variable to the namespace. You can put it outside of the generateId function. For example:
var store = {};
function generateId(numSliders) {
do {
var id = Math.floor(Math.random()*numSliders*1000+1);
} while (store[id])
return id;
}
But in that case you're polluting the namespace with redundant variable.
store inside of the function is used just to shorten the code a little. If you want you can write:
function generateId(numSliders) {
if (!generateId._store) {
generateId._store = {};
}
do {
var id = Math.floor(Math.random()*numSliders*1000+1);
} while (generateId._store[id])
generateId._store[id] = true;
return id;
}

An example using shuffle.
function createNumberSeries(howMany) {
var result = [];
for (var count = 1; count <= howMany; count += 1) {
result.push(count);
}
return result;
}
function shuffle(obj) {
var i = obj.length;
var rnd, tmp;
while (i) {
rnd = Math.floor(Math.random() * i);
i -= 1;
tmp = obj[i];
obj[i] = obj[rnd];
obj[rnd] = tmp;
}
return obj;
}
function createDivs(ids) {
var length = ids.length;
for (var index = 0; index < length; index += 1) {
var div = document.createElement('div');
div.id = ids[index];
div.className = 'initial';
document.body.appendChild(div);
}
}
function colourDivs(howMany) {
for (var index = 1; index <= howMany; index += 1) {
setTimeout((function(id) {
return function() {
document.getElementById(id).className += ' colorMe';
};
}(index)), index * 1000);
}
}
var numSliders = 10;
var sliderIds = shuffle(createNumberSeries(numSliders));
createDivs(sliderIds);
colourDivs(numSliders);
.initial {
height: 10px;
width: 10px;
border-style: solid;
border-width: 1px;
}
.colorMe {
background-color: blue
}

Related

Automatically Change Individual Table Data Using Javascript

I'm trying to mimic the look of a stock-exchange board, but can't seem to automatically change text without stopping another.
I've tried
var text = ["2.0%", "1.7%", "1.9%", "1.8%", "1.9%"];
var counter = 0;
var elem = document.getElementById("n1");
var inst = setInterval (change, 1000);
function change () {
elem.innerHTML = text[counter];
counter++;
var content = document.getElementById("n1");
if (counter >= text.length) {
counter = 0;
}
}
var text = ["-12.0%", "-13.7%", "-13.9%", "-12.8%", "-13.9%"];
var counter = 0;
var elem = document.getElementById("n2");
var inst = setInterval (change, 1000);
function change () {
elem.innerHTML = text[counter];
counter++;
var content = document.getElementById("n2");
if (counter >= text.length) {
counter = 0;
}
}
To no avail.
You can't have two different functions with the same name. One will override the other.
I created a single function that accomplishes your goals by passing in the target element and the data as arguments.
function change(elem, data) {
let counter = 0;
setInterval(function() {
elem.innerHTML = data[counter];
counter++;
if (counter >= data.length) {
counter = 0;
}
}, 1000);
}
change(document.getElementById("n1"), ["2.0%", "1.7%", "1.9%", "1.8%", "1.9%"]);
change(document.getElementById("n2"), ["12.0%", "2.7%", "3.9%", "4.8%", "5.9%"]);
<div id="n1"></div>
<div id="n2"></div>

Store and code values of range sliders as embedded data field in Qualtrics

I would like to store the values of several range sliders, code them as "0"/"1", add them up and store the sum in an embedded data field in Qualtrics. However, with my code below, it only codes and stores the value of the last moved slider.
How can I circumvent that and store the values of all sliders?
Qualtrics.SurveyEngine.addOnload(function()
{
var sliders = document.getElementsByClassName('more-left');
var len = sliders.length;
for ( var i = 0; i < len; i++ ) {
var slider = sliders[i];
slider.addEventListener('change', function() {
updateValue(this);
});
updateValue(slider);
}
function updateValue(slider) {
var id = slider.id;
if (!id) {
return;
}
var val = document.getElementById(id + '_value');
if (val) {
val.innerHTML = slider.value;
}
var question_text = slider.value
if (question_text == 50) {
score = 1;
}
else {
score = 0;
}
Qualtrics.SurveyEngine.setEmbeddedData("Output",score);}});
Thank you!

How to make function work multiple times

var change = function() {
var elem = document.getElementsByTagName("body");
var count = 0;
count++;
var color = "";
var colors = ["#ff6051", "#ff9f51", "#ffdf51", "#b6ff51", "#51adff", "#3e65c1", "#6414ef"];
for (var i = 0; i < colors.length; i++) {
if (count == i + 1) {
color = colors[i];
}
}
elem[0].style.backgroundColor = color;
}
<button onclick="change()">Click me</button>
I want the background color of the body to change when I click the button.
But the number of variable "count" doesn't seem to increase. What should I do to make the number increase?
Declare the variabe count outside the function so that it gets the global scope whenever you update.
DEMO
var count = 0;
var change = function() {
var elem = document.getElementsByTagName("body");
count++;
console.log('##count',count);
var color = "";
var colors = ["#ff6051", "#ff9f51", "#ffdf51", "#b6ff51", "#51adff", "#3e65c1", "#6414ef"];
for (var i = 0; i < colors.length; i++) {
if (count == i + 1) {
color = colors[i];
}
}
elem[0].style.backgroundColor = color;
}
<button onclick="change()">Click me</button>
Without changing your code too much, you can avoid the loop and iterate over the array by comparing the current color. This also avoids holding a count iterator.
Note:
Some browsers return backgroundColor as an rgb value (e.g., rgb( ###, ###, ###)), which is why rgb2hex is used to convert the it to the hex value like that stored in the colors array.
var change = function() {
var el = document.querySelector("body");
var colors = ["#ff6051", "#ff9f51", "#ffdf51", "#b6ff51", "#51adff", "#3e65c1", "#6414ef"];
var currentColor = rgb2hex( el.style.backgroundColor );
var colorIndex = colors.indexOf( currentColor );
// If at last color, cycle back to front
if (colorIndex == colors.length-1)
colorIndex = -1;
el.style.backgroundColor = colors[colorIndex + 1];
}
/** Converts decimal to hex **/
function hex(x) {
return ("0" + parseInt(x).toString(16)).slice(-2);
}
/** Converts rgb string to hex string **/
function rgb2hex(rgb) {
if (rgb.search("rgb") == -1)
return rgb;
else {
rgb = rgb.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+))?\)$/);
return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
}
}
<button onclick="change()">Click me</button>
Each call to your function is resetting count to 0 because you are setting it to zero on the second line of the function.
If you set it to 0 outside the function once, this will solve the count problem.
However, there is an additional problem (that you didn't mention): after counting to 7, you run out of colours in your array because count exceeds the bounds of the array. I would lose the for loop since it is unnecessary (use count to index into the array instead) and just reset count when it reaches the size of the array.
var count = 0;
var colors = ["#ff6051", "#ff9f51", "#ffdf51", "#b6ff51", "#51adff", "#3e65c1", "#6414ef"];
var change = function() {
var elem = document.getElementsByTagName("body");
if (count == colors.length) {
count = 0;
}
elem[0].style.backgroundColor = colors[count];
count++;
}
<button onclick="change()">Click me</button>
The count variable has local scope, so it will not exist after the anonymous function expression referred by change variable finishes execution. For it to sustain its life time across repeated function calls on button click action it should be declared in global scope outside the anonymous function expression:
var count = 0; //now count has global scope.
var change = function() {
var elem = document.getElementsByTagName("body");
count++;
var color = "";
var colors = ["#ff6051", "#ff9f51", "#ffdf51", "#b6ff51", "#51adff", "#3e65c1", "#6414ef"];
for (var i = 0; i < colors.length; i++) {
if (count == i + 1) {
color = colors[i];
}
}
elem[0].style.backgroundColor = color;
}
<button onclick="change()">Click me</button>
Note: You should also consider giving some default color inside the for loop as after 7 clicks it will be setting the backgroundColor to empty string.
var color = "#000000"; //default black color may be
To achieve expected result, use below option
No need of for loop
After 7 clicks , loop runs again
One issue with your code is, first background color will always be skipped due i+1 and after 7 clicks , it comes back to white background
var count = 0;
var colors = ["#ff6051", "#ff9f51", "#ffdf51", "#b6ff51", "#51adff", "#3e65c1", "#6414ef"];
var change = function() {
if(count == colors.length + 1){
count =0;
}
++count;
var elem = document.getElementsByTagName("body");
elem[0].style.backgroundColor = colors[count];
}
https://codepen.io/divyar34/pen/EbZxPm
You can generically count the invocations of any function by "lifting" the function (wrapping it) with a function that just does that.
This leaves you with a simple function that doesn't need to know if it is being counted, which you can wrap with a counter only when you need it.
function countingWrapper(f,reportf)
{
var counter = 0;
return function()
{
reportf( ++counter)
return f.apply(this, arguments);
}
}
function doSomething() { console.log('boo!'); }
function reportSomething(n) { console.log('did something '+ n + ' times.')}
var newDoSomething = countingWrapper(doSomething, reportSomething);
newDoSomething();
newDoSomething();
newSoSomething();

How to a create element and remove last added element after 1 sec intervals?

I have a list of element ids, for example 1, 2, 3 .....
I want to append a div to element 1, wait 1 second, remove the appended div from element 1 and then append a div to element 2, and so on for each element listed in seqIDs
Below is my code but I can't figure out how to remove the previous appended element or how to delay the loop by 1 second. I'm using vanilla javascript, so please no jQuery answers
for (var i = 0, len = seqIDs.length; i < len; i++) {
var newDiv = document.createElement('div');
document.getElementById(seqIDs[i]).appendChild(newDiv);
setTimeout(this.reset, 1000);
}
You can use setTimeout() to walk through the sequence, removing one and adding another on each timer call until you get to the end of the array:
(function() {
var cntr = 0;
var lastItem;
function next() {
if (lastItem) {
lastItem.parentNode.removeChild(lastItem);
lastItem = null;
}
if (cntr < seqIDs.length) {
var newDiv = document.createElement("div");
document.getElementById(seqIDs[cntr++]).appendChild(newDiv);
lastItem = newDiv;
setTimeout(next, 1000);
}
}
next();
})();
If you want it to just repeat itself over and over again, you can put it in a function that takes an argument for whether to repeat itself and then wrap cntr back to 0:
function cycleDiv(forever) {
var cntr = 0;
var lastItem;
function next() {
if (lastItem) {
lastItem.parentNode.removeChild(lastItem);
lastItem = null;
}
if (forever && cntr >= seqIDs.length) {
// wrap back to zero
cntr = 0;
}
if (cntr < seqIDs.length) {
var newDiv = document.createElement("div");
document.getElementById(seqIDs[cntr++]).appendChild(newDiv);
lastItem = newDiv;
setTimeout(next, 1000);
}
}
next();
}
cycleDiv(true);
To make one that you can start or stop at any time, you could do this:
function cycleDiv(forever) {
var cntr = 0;
var lastItem;
function next() {
if (lastItem) {
lastItem.parentNode.removeChild(lastItem);
lastItem = null;
}
if (forever && cntr >= seqIDs.length) {
// wrap back to zero
cntr = 0;
}
if (cntr < seqIDs.length) {
var newDiv = document.createElement("div");
document.getElementById(seqIDs[cntr++]).appendChild(newDiv);
lastItem = newDiv;
this.timer = setTimeout(next, 1000);
}
}
this.stop = function() {
clearTimeout(this.timer);
this.timer = null;
}
this.start = function() {
if (!this.timer) {
next();
}
}
}
var cycler = new cycleDiv(true);
cycler.start();
// then some time later
cycler.stop();
Also, it's a bit inefficient to keep creating and removing a new div element every second. I don't know what you're doing with the content of this div (since there's nothing in it in this code), but you could just have one div that you move from one parent to the next rather than continually making new div elements.

How to disable buttons after x amount of clicks in js?

I am trying to use Javascript to disable a button after it is clicked x amount of times. For simplicity sake lets say x = 2 for now. I cannot seem to get the counter to increment. Thank You for any help!
var $ = function (id) {
return document.getElementById(id);
}
window.onload = function () {
coke.onclick = function(){
var count =0;
if (count >= 1)
{
coke.disabled = true;
}
else
count++;
};
}
Where "coke" is the element ID. If i get rid of the if statement and just have coke.disabled = true, of course it works and disables after one click. I'm sure there is a core concept I am missing.
Thank You
This is happening because each time the onclick event is fired, your var count is being assigned to 0, so it will never be greater than or equal to one in your function. If you initialize the count var outside of the onclick function, it will behave as expected.
window.onload = function () {
var count = 0;
coke.onclick = function(){
if (count >= 1)
{
coke.disabled = true;
}
else
count++;
};
}
You need to define count outside the scope of your onclick function:
var $ = function (id) {
return document.getElementById(id);
}
var count = 0; // set initial count to 0
window.onload = function () {
coke.onclick = function(){
if (count >= 1)
{
coke.disabled = true;
}
else
count++;
};
}

Categories