Random sort array - javascript

I am trying to shuffle the original deck of cards, then subtract 2 cards from the shuffled deck, being player one's cards. Then before I move to player 2, I would like to shuffle the cards again, whose length should be at 50(52-2). The second player I will repeat the process(50-2).
Notice the order of the third console log array before uncommenting the fourth console log and freshDeck__01 in the JavaScript. The order is good before uncommenting. I want that order, and then to shuffle.
let playerone = document.querySelector(".dealItP1");
let playertwo = document.querySelector(".dealItP2");
let playerthree = document.querySelector(".dealItP3");
let playerfour = document.querySelector(".dealItP4");
let deck = ["2 Club","2 Spade","2 Diamond","2 Heart","3 Club","3 Spade","3 Diamond","3 Heart","4 Club","4 Spade","4 Diamond","4 Heart","5 Club","5 Spade","5 Diamond","5 Heart","6 Club","6 Spade","6 Diamond","6 Heart","7 Club","7 Spade","7 Diamond","7 Heart","8 Club","8 Spade","8 Diamond","8 Heart","9 Club","9 Spade","9 Diamond","9 Heart","10 Club","10 Spade","10 Diamond","10 Heart","Jack Club","Jack Spade","Jack Diamond","Jack Heart","Queen Club","Queen Spade","Queen Diamond","Queen Heart","King Club","King Spade","King Diamond","King Heart","Ace Club","Ace Spade","Ace Diamond","Ace Heart"];
let originaldeck = [...deck];
function dealIt(){
function shuffle(deck) {
var currentIndex = deck.length, temporaryValue, randomIndex;
while (0 !== currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
temporaryValue = deck[currentIndex];
deck[currentIndex] = deck[randomIndex];
deck[randomIndex] = temporaryValue;
}
return deck;
}
var arr = deck;
let freshDeck_00 = shuffle(arr); //length = 52 *Working* shuffled//
let p1Deal = freshDeck_00.filter(function(value, index, arr){return index < 2;}); //length=2 *Working* PlayerOne delt cards//
let loadedDeck_00 = freshDeck_00.filter(x => !p1Deal.includes(x)).concat(p1Deal.filter(x => !freshDeck_00.includes(x)));
//length = 50 *Working* Symmetrical Difference between p1Deal and freshdeck_00 set to loadedDeck_00 ready to be shuffled again//
playerone.innerHTML= p1Deal;
// let freshDeck_01 = shuffle(loadedDeck_00);//
//*IMPORTANT* WORKING UP TO THIS POINT WITH THE THRE CONSOLE LOGS, BUT WHEN UNCOMMENTING FRESHDECK_01 AND FORTH CONSOLE LOG, NOTICE THE DIFFERENCE IN ORDER OF LOADEDDECK__00(THIRD CONSOLE LOG)//
console.log(freshDeck_00);
console.log(p1Deal);
console.log(loadedDeck_00);
//console.log(freshDeck_01);//
}
.main{
box-sizing: border-box;
border: 3px solid green;
height: 1000px;
width: 1000px;
position: absolute;
background-color: black;
}
.title{
box-sizing: border-box;
border: 3px green solid;
height: 100px;
width: 200px;
position: absolute;
top: 10%;
left: 50%;
background-color: green;
opacity: .2;
font-family: coniferous, sans-serif;
font-style: normal;
font-weight: 300;
}
.P1{
box-sizing: border-box;
border: 3px green solid;
height: 100px;
width: 100px;
position: absolute;
top: 30%;
left: 45%;
background-color: green;
opacity: .5;
font-family: coniferous, sans-serif;
font-style: normal;
font-weight: 300;
color: red;
}
.P2{
box-sizing: border-box;
border: 3px green solid;
height: 100px;
width: 100px;
position: absolute;
top: 45%;
left: 10%;
background-color: green;
opacity: .5;
font-family: coniferous, sans-serif;
font-style: normal;
font-weight: 300;
color: red;
}
.P3{
box-sizing: border-box;
border: 3px green solid;
height: 100px;
width: 100px;
position: absolute;
top: 60%;
left: 45%;
background-color: green;
opacity: .5;
font-family: coniferous, sans-serif;
font-style: normal;
font-weight: 300;
color: red;
}
.P4{
box-sizing: border-box;
border: 3px green solid;
height: 100px;
width: 100px;
position: absolute;
top: 45%;
left: 80%;
background-color: green;
opacity: .5;
font-family: coniferous, sans-serif;
font-style: normal;
font-weight: 300;
color: red;
}
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Untitled Document</title>
<link href="pokerTryOne.css" rel="stylesheet" type="text/css">
</head>
<body>
<div class="main">
<header><h1 class="title"><button onClick="dealIt()">Click Button to Deal</button></h1></header>
<div class="P1"><p>Pot:</p><div class="dealItP1"></div></div>
<div class="P2"><p>Pot:</p><div class="dealItP2"></div></div>
<div class="P3"><p>Pot:</p><div class="dealItP3"></div></div>
<div class="P4"><p>Pot:</p><div class="dealItP4"></div></div>
<div class="dealBet"></div>
<div class="flopIt"></div>
<div class="flopBet"></div>
<div class="turnIt"></div>
<div class="turnBet"></div>
<div class="riverIt"></div>
<div class="riverBet"></div>
</div>
<script type="text/javascript" src="pokerTryOne.js"></script>
</body>
</html>

when I wanted to reassign freshdeck_01 by randomizing it again I
receive the same array as before.
No you do not receive an array that is ordered the same way as before.
The issue here is that Array.prototype.sort() sorts the array in-place. This means that you are shuffling your original array freshdeck_01 and then assigning a reference to that very same (now shuffled) array to another variable freshdeck_01_fresh. If you actually inspect your array before running your shuffling function, you will see that the order gets changed:
let deck = [1,2,3,4,5,6,7,8]
console.log(deck);
deck.sort(function(a, b){return 0.5 - Math.random()});
console.log(deck);
If you need to keep a copy of your unshuffled array, you can use a decomposing assignment [...x] to do that. Simple assignment would not work, as it would only create a reference to the same array.
let deck = [1,2,3,4,5,6,7,8]
let copyOfOrigDeck = [...deck];
let notASeparateCopy = deck;
console.log(deck);
deck.sort(function(a, b){return 0.5 - Math.random()});
console.log('unshuffled: ' + copyOfOrigDeck);
console.log('shuffled: ' + deck);
console.log('notASeparateCopy: ' + notASeparateCopy);
All that being said, there are probably better ways to shuffle array elements than your approach. I am no expert in that field, so I will leave that research up to you.

Sort() isn't the correct function for this, shuffle has been covered extensively check it out here
this
and many other places on the net.

const numbers = [1,2,3,4,5,6,7,8,9,10]
// Returns a random value from a list.
const sampleFromList = list => list[Math.floor(Math.random() * list.length)]
const shuffle = (
list,
// Creates an array with every index of the original list.
availableIndexes = [...list].map((n, i) => i),
shuffledList = [],
) => {
// Asks for a random index from the whitelist of available indexes.
const availableIndex = sampleFromList(availableIndexes)
// Adds the value of what's in the original list in the random whitelisted index.
shuffledList = [...shuffledList, list[availableIndex]]
// Filters out the used index, so is not used again.
availableIndexes = availableIndexes.filter(n => n !== availableIndex)
return (
// If there are available indexes, use a recursive function to continue shuffling. Otherwise return the shuffled list.
availableIndexes.length
? shuffle(list, availableIndexes, shuffledList)
: shuffledList
)
}
console.log(shuffle(numbers))

Related

Accessible input elements without label [duplicate]

Is it possible to make a HTML5 slider with two input values, for example to select a price range? If so, how can it be done?
I've been looking for a lightweight, dependency free dual slider for some time (it seemed crazy to import jQuery just for this) and there don't seem to be many out there. I ended up modifying #Wildhoney's code a bit and really like it.
function getVals(){
// Get slider values
var parent = this.parentNode;
var slides = parent.getElementsByTagName("input");
var slide1 = parseFloat( slides[0].value );
var slide2 = parseFloat( slides[1].value );
// Neither slider will clip the other, so make sure we determine which is larger
if( slide1 > slide2 ){ var tmp = slide2; slide2 = slide1; slide1 = tmp; }
var displayElement = parent.getElementsByClassName("rangeValues")[0];
displayElement.innerHTML = slide1 + " - " + slide2;
}
window.onload = function(){
// Initialize Sliders
var sliderSections = document.getElementsByClassName("range-slider");
for( var x = 0; x < sliderSections.length; x++ ){
var sliders = sliderSections[x].getElementsByTagName("input");
for( var y = 0; y < sliders.length; y++ ){
if( sliders[y].type ==="range" ){
sliders[y].oninput = getVals;
// Manually trigger event first time to display values
sliders[y].oninput();
}
}
}
}
section.range-slider {
position: relative;
width: 200px;
height: 35px;
text-align: center;
}
section.range-slider input {
pointer-events: none;
position: absolute;
overflow: hidden;
left: 0;
top: 15px;
width: 200px;
outline: none;
height: 18px;
margin: 0;
padding: 0;
}
section.range-slider input::-webkit-slider-thumb {
pointer-events: all;
position: relative;
z-index: 1;
outline: 0;
}
section.range-slider input::-moz-range-thumb {
pointer-events: all;
position: relative;
z-index: 10;
-moz-appearance: none;
width: 9px;
}
section.range-slider input::-moz-range-track {
position: relative;
z-index: -1;
background-color: rgba(0, 0, 0, 1);
border: 0;
}
section.range-slider input:last-of-type::-moz-range-track {
-moz-appearance: none;
background: none transparent;
border: 0;
}
section.range-slider input[type=range]::-moz-focus-outer {
border: 0;
}
<!-- This block can be reused as many times as needed -->
<section class="range-slider">
<span class="rangeValues"></span>
<input value="5" min="0" max="15" step="0.5" type="range">
<input value="10" min="0" max="15" step="0.5" type="range">
</section>
No, the HTML5 range input only accepts one input. I would recommend you to use something like the jQuery UI range slider for that task.
Coming late, but noUiSlider avoids having a jQuery-ui dependency, which the accepted answer does not. Its only "caveat" is IE support is for IE9 and newer, if legacy IE is a deal breaker for you.
It's also free, open source and can be used in commercial projects without restrictions.
Installation: Download noUiSlider, extract the CSS and JS file somewhere in your site file system, and then link to the CSS from head and to JS from body:
<!-- In <head> -->
<link href="nouislider.min.css" rel="stylesheet">
<!-- In <body> -->
<script src="nouislider.min.js"></script>
Example usage: Creates a slider which goes from 0 to 100, and starts set to 20-80.
HTML:
<div id="slider">
</div>
JS:
var slider = document.getElementById('slider');
noUiSlider.create(slider, {
start: [20, 80],
connect: true,
range: {
'min': 0,
'max': 100
}
});
Sure you can simply use two sliders overlaying each other and add a bit of javascript (actually not more than 5 lines) that the selectors are not exceeding the min/max values (like in #Garys) solution.
Attached you'll find a short snippet adapted from a current project including some CSS3 styling to show what you can do (webkit only). I also added some labels to display the selected values.
It uses JQuery but a vanillajs version is no magic though.
#Update: The code below was just a proof of concept. Due to many requests I've added a possible solution for Mozilla Firefox (without changing the original code). You may want to refractor the code below before using it.
(function() {
function addSeparator(nStr) {
nStr += '';
var x = nStr.split('.');
var x1 = x[0];
var x2 = x.length > 1 ? '.' + x[1] : '';
var rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' + '.' + '$2');
}
return x1 + x2;
}
function rangeInputChangeEventHandler(e){
var rangeGroup = $(this).attr('name'),
minBtn = $(this).parent().children('.min'),
maxBtn = $(this).parent().children('.max'),
range_min = $(this).parent().children('.range_min'),
range_max = $(this).parent().children('.range_max'),
minVal = parseInt($(minBtn).val()),
maxVal = parseInt($(maxBtn).val()),
origin = $(this).context.className;
if(origin === 'min' && minVal > maxVal-5){
$(minBtn).val(maxVal-5);
}
var minVal = parseInt($(minBtn).val());
$(range_min).html(addSeparator(minVal*1000) + ' €');
if(origin === 'max' && maxVal-5 < minVal){
$(maxBtn).val(5+ minVal);
}
var maxVal = parseInt($(maxBtn).val());
$(range_max).html(addSeparator(maxVal*1000) + ' €');
}
$('input[type="range"]').on( 'input', rangeInputChangeEventHandler);
})();
body{
font-family: sans-serif;
font-size:14px;
}
input[type='range'] {
width: 210px;
height: 30px;
overflow: hidden;
cursor: pointer;
outline: none;
}
input[type='range'],
input[type='range']::-webkit-slider-runnable-track,
input[type='range']::-webkit-slider-thumb {
-webkit-appearance: none;
background: none;
}
input[type='range']::-webkit-slider-runnable-track {
width: 200px;
height: 1px;
background: #003D7C;
}
input[type='range']:nth-child(2)::-webkit-slider-runnable-track{
background: none;
}
input[type='range']::-webkit-slider-thumb {
position: relative;
height: 15px;
width: 15px;
margin-top: -7px;
background: #fff;
border: 1px solid #003D7C;
border-radius: 25px;
z-index: 1;
}
input[type='range']:nth-child(1)::-webkit-slider-thumb{
z-index: 2;
}
.rangeslider{
position: relative;
height: 60px;
width: 210px;
display: inline-block;
margin-top: -5px;
margin-left: 20px;
}
.rangeslider input{
position: absolute;
}
.rangeslider{
position: absolute;
}
.rangeslider span{
position: absolute;
margin-top: 30px;
left: 0;
}
.rangeslider .right{
position: relative;
float: right;
margin-right: -5px;
}
/* Proof of concept for Firefox */
#-moz-document url-prefix() {
.rangeslider::before{
content:'';
width:100%;
height:2px;
background: #003D7C;
display:block;
position: relative;
top:16px;
}
input[type='range']:nth-child(1){
position:absolute;
top:35px !important;
overflow:visible !important;
height:0;
}
input[type='range']:nth-child(2){
position:absolute;
top:35px !important;
overflow:visible !important;
height:0;
}
input[type='range']::-moz-range-thumb {
position: relative;
height: 15px;
width: 15px;
margin-top: -7px;
background: #fff;
border: 1px solid #003D7C;
border-radius: 25px;
z-index: 1;
}
input[type='range']:nth-child(1)::-moz-range-thumb {
transform: translateY(-20px);
}
input[type='range']:nth-child(2)::-moz-range-thumb {
transform: translateY(-20px);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<div class="rangeslider">
<input class="min" name="range_1" type="range" min="1" max="100" value="10" />
<input class="max" name="range_1" type="range" min="1" max="100" value="90" />
<span class="range_min light left">10.000 €</span>
<span class="range_max light right">90.000 €</span>
</div>
Actually I used my script in html directly. But in javascript when you add oninput event listener for this event it gives the data automatically.You just need to assign the value as per your requirement.
[slider] {
width: 300px;
position: relative;
height: 5px;
margin: 45px 0 10px 0;
}
[slider] > div {
position: absolute;
left: 13px;
right: 15px;
height: 5px;
}
[slider] > div > [inverse-left] {
position: absolute;
left: 0;
height: 5px;
border-radius: 10px;
background-color: #CCC;
margin: 0 7px;
}
[slider] > div > [inverse-right] {
position: absolute;
right: 0;
height: 5px;
border-radius: 10px;
background-color: #CCC;
margin: 0 7px;
}
[slider] > div > [range] {
position: absolute;
left: 0;
height: 5px;
border-radius: 14px;
background-color: #d02128;
}
[slider] > div > [thumb] {
position: absolute;
top: -7px;
z-index: 2;
height: 20px;
width: 20px;
text-align: left;
margin-left: -11px;
cursor: pointer;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.4);
background-color: #FFF;
border-radius: 50%;
outline: none;
}
[slider] > input[type=range] {
position: absolute;
pointer-events: none;
-webkit-appearance: none;
z-index: 3;
height: 14px;
top: -2px;
width: 100%;
opacity: 0;
}
div[slider] > input[type=range]:focus::-webkit-slider-runnable-track {
background: transparent;
border: transparent;
}
div[slider] > input[type=range]:focus {
outline: none;
}
div[slider] > input[type=range]::-webkit-slider-thumb {
pointer-events: all;
width: 28px;
height: 28px;
border-radius: 0px;
border: 0 none;
background: red;
-webkit-appearance: none;
}
div[slider] > input[type=range]::-ms-fill-lower {
background: transparent;
border: 0 none;
}
div[slider] > input[type=range]::-ms-fill-upper {
background: transparent;
border: 0 none;
}
div[slider] > input[type=range]::-ms-tooltip {
display: none;
}
[slider] > div > [sign] {
opacity: 0;
position: absolute;
margin-left: -11px;
top: -39px;
z-index:3;
background-color: #d02128;
color: #fff;
width: 28px;
height: 28px;
border-radius: 28px;
-webkit-border-radius: 28px;
align-items: center;
-webkit-justify-content: center;
justify-content: center;
text-align: center;
}
[slider] > div > [sign]:after {
position: absolute;
content: '';
left: 0;
border-radius: 16px;
top: 19px;
border-left: 14px solid transparent;
border-right: 14px solid transparent;
border-top-width: 16px;
border-top-style: solid;
border-top-color: #d02128;
}
[slider] > div > [sign] > span {
font-size: 12px;
font-weight: 700;
line-height: 28px;
}
[slider]:hover > div > [sign] {
opacity: 1;
}
<div slider id="slider-distance">
<div>
<div inverse-left style="width:70%;"></div>
<div inverse-right style="width:70%;"></div>
<div range style="left:0%;right:0%;"></div>
<span thumb style="left:0%;"></span>
<span thumb style="left:100%;"></span>
<div sign style="left:0%;">
<span id="value">0</span>
</div>
<div sign style="left:100%;">
<span id="value">100</span>
</div>
</div>
<input type="range" value="0" max="100" min="0" step="1" oninput="
this.value=Math.min(this.value,this.parentNode.childNodes[5].value-1);
let value = (this.value/parseInt(this.max))*100
var children = this.parentNode.childNodes[1].childNodes;
children[1].style.width=value+'%';
children[5].style.left=value+'%';
children[7].style.left=value+'%';children[11].style.left=value+'%';
children[11].childNodes[1].innerHTML=this.value;" />
<input type="range" value="100" max="100" min="0" step="1" oninput="
this.value=Math.max(this.value,this.parentNode.childNodes[3].value-(-1));
let value = (this.value/parseInt(this.max))*100
var children = this.parentNode.childNodes[1].childNodes;
children[3].style.width=(100-value)+'%';
children[5].style.right=(100-value)+'%';
children[9].style.left=value+'%';children[13].style.left=value+'%';
children[13].childNodes[1].innerHTML=this.value;" />
</div>
The question was: "Is it possible to make a HTML5 slider with two input values, for example to select a price range? If so, how can it be done?"
In 2020 it is possible to create a fully accessible, native, non-jquery HTML5 slider with two thumbs for price ranges. If found this posted after I already created this solution and I thought that it would be nice to share my implementation here.
This implementation has been tested on mobile Chrome and Firefox (Android) and Chrome and Firefox (Linux). I am not sure about other platforms, but it should be quite good. I would love to get your feedback and improve this solution.
This solution allows multiple instances on one page and it consists of just two inputs (each) with descriptive labels for screen readers. You can set the thumb size in the amount of grid labels. Also, you can use touch, keyboard and mouse to interact with the slider. The value is updated during adjustment, due to the 'on input' event listener.
My first approach was to overlay the sliders and clip them. However, that resulted in complex code with a lot of browser dependencies. Then I recreated the solution with two sliders that were 'inline'. This is the solution you will find below.
var thumbsize = 14;
function draw(slider,splitvalue) {
/* set function vars */
var min = slider.querySelector('.min');
var max = slider.querySelector('.max');
var lower = slider.querySelector('.lower');
var upper = slider.querySelector('.upper');
var legend = slider.querySelector('.legend');
var thumbsize = parseInt(slider.getAttribute('data-thumbsize'));
var rangewidth = parseInt(slider.getAttribute('data-rangewidth'));
var rangemin = parseInt(slider.getAttribute('data-rangemin'));
var rangemax = parseInt(slider.getAttribute('data-rangemax'));
/* set min and max attributes */
min.setAttribute('max',splitvalue);
max.setAttribute('min',splitvalue);
/* set css */
min.style.width = parseInt(thumbsize + ((splitvalue - rangemin)/(rangemax - rangemin))*(rangewidth - (2*thumbsize)))+'px';
max.style.width = parseInt(thumbsize + ((rangemax - splitvalue)/(rangemax - rangemin))*(rangewidth - (2*thumbsize)))+'px';
min.style.left = '0px';
max.style.left = parseInt(min.style.width)+'px';
min.style.top = lower.offsetHeight+'px';
max.style.top = lower.offsetHeight+'px';
legend.style.marginTop = min.offsetHeight+'px';
slider.style.height = (lower.offsetHeight + min.offsetHeight + legend.offsetHeight)+'px';
/* correct for 1 off at the end */
if(max.value>(rangemax - 1)) max.setAttribute('data-value',rangemax);
/* write value and labels */
max.value = max.getAttribute('data-value');
min.value = min.getAttribute('data-value');
lower.innerHTML = min.getAttribute('data-value');
upper.innerHTML = max.getAttribute('data-value');
}
function init(slider) {
/* set function vars */
var min = slider.querySelector('.min');
var max = slider.querySelector('.max');
var rangemin = parseInt(min.getAttribute('min'));
var rangemax = parseInt(max.getAttribute('max'));
var avgvalue = (rangemin + rangemax)/2;
var legendnum = slider.getAttribute('data-legendnum');
/* set data-values */
min.setAttribute('data-value',rangemin);
max.setAttribute('data-value',rangemax);
/* set data vars */
slider.setAttribute('data-rangemin',rangemin);
slider.setAttribute('data-rangemax',rangemax);
slider.setAttribute('data-thumbsize',thumbsize);
slider.setAttribute('data-rangewidth',slider.offsetWidth);
/* write labels */
var lower = document.createElement('span');
var upper = document.createElement('span');
lower.classList.add('lower','value');
upper.classList.add('upper','value');
lower.appendChild(document.createTextNode(rangemin));
upper.appendChild(document.createTextNode(rangemax));
slider.insertBefore(lower,min.previousElementSibling);
slider.insertBefore(upper,min.previousElementSibling);
/* write legend */
var legend = document.createElement('div');
legend.classList.add('legend');
var legendvalues = [];
for (var i = 0; i < legendnum; i++) {
legendvalues[i] = document.createElement('div');
var val = Math.round(rangemin+(i/(legendnum-1))*(rangemax - rangemin));
legendvalues[i].appendChild(document.createTextNode(val));
legend.appendChild(legendvalues[i]);
}
slider.appendChild(legend);
/* draw */
draw(slider,avgvalue);
/* events */
min.addEventListener("input", function() {update(min);});
max.addEventListener("input", function() {update(max);});
}
function update(el){
/* set function vars */
var slider = el.parentElement;
var min = slider.querySelector('#min');
var max = slider.querySelector('#max');
var minvalue = Math.floor(min.value);
var maxvalue = Math.floor(max.value);
/* set inactive values before draw */
min.setAttribute('data-value',minvalue);
max.setAttribute('data-value',maxvalue);
var avgvalue = (minvalue + maxvalue)/2;
/* draw */
draw(slider,avgvalue);
}
var sliders = document.querySelectorAll('.min-max-slider');
sliders.forEach( function(slider) {
init(slider);
});
* {padding: 0; margin: 0;}
body {padding: 40px;}
.min-max-slider {position: relative; width: 200px; text-align: center; margin-bottom: 50px;}
.min-max-slider > label {display: none;}
span.value {height: 1.7em; font-weight: bold; display: inline-block;}
span.value.lower::before {content: "€"; display: inline-block;}
span.value.upper::before {content: "- €"; display: inline-block; margin-left: 0.4em;}
.min-max-slider > .legend {display: flex; justify-content: space-between;}
.min-max-slider > .legend > * {font-size: small; opacity: 0.25;}
.min-max-slider > input {cursor: pointer; position: absolute;}
/* webkit specific styling */
.min-max-slider > input {
-webkit-appearance: none;
outline: none!important;
background: transparent;
background-image: linear-gradient(to bottom, transparent 0%, transparent 30%, silver 30%, silver 60%, transparent 60%, transparent 100%);
}
.min-max-slider > input::-webkit-slider-thumb {
-webkit-appearance: none; /* Override default look */
appearance: none;
width: 14px; /* Set a specific slider handle width */
height: 14px; /* Slider handle height */
background: #eee; /* Green background */
cursor: pointer; /* Cursor on hover */
border: 1px solid gray;
border-radius: 100%;
}
.min-max-slider > input::-webkit-slider-runnable-track {cursor: pointer;}
<div class="min-max-slider" data-legendnum="2">
<label for="min">Minimum price</label>
<input id="min" class="min" name="min" type="range" step="1" min="0" max="3000" />
<label for="max">Maximum price</label>
<input id="max" class="max" name="max" type="range" step="1" min="0" max="3000" />
</div>
Note that you should keep the step size to 1 to prevent the values to change due to redraws/redraw bugs.
View online at: https://codepen.io/joosts/pen/rNLdxvK
2022 - Accessible solution - 30 second solution to implement
This solution builds off of this answer by #JoostS. Accessibility is something none of the answers have focused on and that is a problem, so I built off of the above answer by making it more accessible & extensible since it had some flaws.
Usage is very simple:
Use the CDN or host the script locally: https://cdn.jsdelivr.net/gh/maxshuty/accessible-web-components/dist/simpleRange.min.js
Add this element to your template or HTML: <range-selector min-range="0" max-range="1000" />
Hook into it by listening for the range-changed event (or whatever event-name-to-emit-on-change you pass in)
That's it. View the full demo here. You can easily customize it by simply applying attributes like inputs-for-labels to use inputs instead of labels, slider-color to adjust the color, and so much more!
Here is a fiddle:
window.addEventListener('range-changed', (e) => {console.log(`Range changed for: ${e.detail.sliderId}. Min/Max range values are available in this object too`)})
<script src="https://cdn.jsdelivr.net/gh/maxshuty/accessible-web-components#latest/dist/simpleRange.min.js"></script>
<div>
<range-selector
id="rangeSelector1"
min-label="Minimum"
max-label="Maximum"
min-range="1000"
max-range="2022"
number-of-legend-items-to-show="6"
/>
</div>
<div>
<range-selector
id="rangeSelector1"
min-label="Minimum"
max-label="Maximum"
min-range="1"
max-range="500"
number-of-legend-items-to-show="3"
inputs-for-labels
/>
</div>
<div>
<range-selector
id="rangeSelector2"
min-label="Minimum"
max-label="Maximum"
min-range="1000"
max-range="2022"
number-of-legend-items-to-show="3"
slider-color="#6b5b95"
/>
</div>
<div>
<range-selector
id="rangeSelector3"
min-label="Minimum"
max-label="Maximum"
min-range="1000"
max-range="2022"
hide-label
hide-legend
/>
</div>
I decided to address the issues of the linked answer like the labels using display: none (bad for a11y), no visual focus on the slider, etc., and improve the code by cleaning up event listeners and making it much more dynamic and extensible.
I created this tiny library with many options to customize colors, event names, easily hook into it, make the accessible labels i18n capable and much more. Here it is in a fiddle if you want to play around.
You can easily customize the number of legend items it shows, hide or show the labels and legend, and customize the colors of everything, including the focus color like this.
Example using several of the props:
<range-selector
min-label="i18n Minimum Range"
max-label="i18n Maximum Range"
min-range="5"
max-range="555"
number-of-legend-items-to-show="6"
event-name-to-emit-on-change="my-custom-range-changed-event"
slider-color="orange"
circle-color="#f7cac9"
circle-border-color="#083535"
circle-focus-border-color="#3ec400"
/>
Then in your script:
window.addEventListener('my-custom-range-changed-event', (e) => { const data = e.detail; });
Finally if you see that this is missing something that you need I made it very easy to customize this library.
Simply copy this file and at the top you can see cssHelpers and constants objects that contain most of the variables you would likely want to further customize.
Since I built this with a Native Web Component I have taken advantage of disconnectedCallback and other hooks to clean up event listeners and set things up.
Here is a reusable double range slider implementation, base on tutorial Double Range Slider by Coding Artist
near native UI, Chrome/Firefox/Safari compatible
API EventTarget based, with change/input events, minGap/maxGap properties
let $ = (s, c = document) => c.querySelector(s);
let $$ = (s, c = document) => Array.prototype.slice.call(c.querySelectorAll(s));
class DoubleRangeSlider extends EventTarget {
#minGap = 0;
#maxGap = Number.MAX_SAFE_INTEGER;
#inputs;
style = {
trackColor: '#dadae5',
rangeColor: '#3264fe',
};
constructor(container){
super();
let inputs = $$('input[type="range"]', container);
if(inputs.length !== 2){
throw new RangeError('2 range inputs expected');
}
let [input1, input2] = inputs;
if(input1.min >= input1.max || input2.min >= input2.max){
throw new RangeError('range min should be less than max');
}
if(input1.max > input2.max || input1.min > input2.min){
throw new RangeError('input1\'s max/min should not be greater than input2\'s max/min');
}
this.#inputs = inputs;
let sliderTrack = $('.slider-track', container);
let lastValue1 = input1.value;
input1.addEventListener('input', (e) => {
let value1 = +input1.value;
let value2 = +input2.value;
let minGap = this.#minGap;
let maxGap = this.#maxGap;
let gap = value2 - value1;
let newValue1 = value1;
if(gap < minGap){
newValue1 = value2 - minGap;
}else if(gap > maxGap){
newValue1 = value2 - maxGap;
}
input1.value = newValue1;
if(input1.value !== lastValue1){
lastValue1 = input1.value;
passEvent(e);
fillColor();
}
});
let lastValue2 = input2.value;
input2.addEventListener('input', (e) => {
let value1 = +input1.value;
let value2 = +input2.value;
let minGap = this.#minGap;
let maxGap = this.#maxGap;
let gap = value2 - value1;
let newValue2 = value2;
if(gap < minGap){
newValue2 = value1 + minGap;
}else if(gap > maxGap){
newValue2 = value1 + maxGap;
}
input2.value = newValue2;
if(input2.value !== lastValue2){
lastValue2 = input2.value;
passEvent(e);
fillColor();
}
});
let passEvent = (e) => {
this.dispatchEvent(new e.constructor(e.type, e));
};
input1.addEventListener('change', passEvent);
input2.addEventListener('change', passEvent);
let fillColor = () => {
let overallMax = +input2.max;
let overallMin = +input1.min;
let overallRange = overallMax - overallMin;
let left1 = ((input1.value - overallMin) / overallRange * 100) + '%';
let left2 = ((input2.value - overallMin) / overallRange * 100) + '%';
let {trackColor, rangeColor} = this.style;
sliderTrack.style.background = `linear-gradient(to right, ${trackColor} ${left1}, ${rangeColor} ${left1}, ${rangeColor} ${left2}, ${trackColor} ${left2})`;
};
let init = () => {
let overallMax = +input2.max;
let overallMin = +input1.min;
let overallRange = overallMax - overallMin;
let range1 = input1.max - overallMin;
let range2 = overallMax - input2.min;
input1.style.left = '0px';
input1.style.width = (range1 / overallRange * 100) + '%';
input2.style.right = '0px';
input2.style.width = (range2 / overallRange * 100) + '%';
fillColor();
};
init();
}
get minGap(){
return this.#minGap;
}
set minGap(v){
this.#minGap = v;
}
get maxGap(){
return this.#maxGap;
}
set maxGap(v){
this.#maxGap = v;
}
get values(){
return this.#inputs.map((el) => el.value);
}
set values(values){
if(values.length !== 2 || !values.every(isFinite))
throw new RangeError();
let [input1, input2] = this.#inputs;
let [value1, value2] = values;
if(value1 > input1.max || value1 < input1.min)
throw new RangeError('invalid value for input1');
if(value2 > input2.max || value2 < input2.min)
throw new RangeError('invalid value for input2');
input1.value = value1;
input2.value = value2;
}
get inputs(){
return this.#inputs;
}
get overallMin(){
return this.#inputs[0].min;
}
get overallMax(){
return this.#inputs[1].max;
}
}
function main(){
let container = $('.slider-container');
let slider = new DoubleRangeSlider(container);
slider.minGap = 30;
slider.maxGap = 70;
let inputs = $$('input[name="a"]');
let outputs = $$('output[name="a"]');
outputs[0].value = inputs[0].value;
outputs[1].value = inputs[1].value;
slider.addEventListener('input', (e) => {
let values = slider.values;
outputs[0].value = values[0];
outputs[1].value = values[1];
});
slider.addEventListener('change', (e) => {
let values = slider.values;
console.log('change', values);
outputs[0].value = values[0];
outputs[1].value = values[1];
});
}
document.addEventListener('DOMContentLoaded', main);
.slider-container {
display: inline-block;
position: relative;
width: 360px;
height: 28px;
}
.slider-track {
width: 100%;
height: 5px;
position: absolute;
margin: auto;
top: 0;
bottom: 0;
border-radius: 5px;
}
.slider-container>input[type="range"] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
position: absolute;
margin: auto;
top: 0;
bottom: 0;
width: 100%;
outline: none;
background-color: transparent;
pointer-events: none;
}
.slider-container>input[type="range"]::-webkit-slider-runnable-track {
-webkit-appearance: none;
height: 5px;
}
.slider-container>input[type="range"]::-moz-range-track {
-moz-appearance: none;
height: 5px;
}
.slider-container>input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
margin-top: -9px;
height: 1.7em;
width: 1.7em;
background-color: #3264fe;
cursor: pointer;
pointer-events: auto;
border-radius: 50%;
}
.slider-container>input[type="range"]::-moz-range-thumb {
-moz-appearance: none;
height: 1.7em;
width: 1.7em;
cursor: pointer;
border: none;
border-radius: 50%;
background-color: #3264fe;
pointer-events: auto;
}
.slider-container>input[type="range"]:active::-webkit-slider-thumb {
background-color: #ffffff;
border: 3px solid #3264fe;
}
<h3>Double Range Slider, Reusable Edition</h3>
<div class="slider-container">
<div class="slider-track"></div>
<input type="range" name="a" min="-130" max="-30" step="1" value="-100" autocomplete="off" />
<input type="range" name="a" min="-60" max="0" step="2" value="-30" autocomplete="off" />
</div>
<div>
<output name="a"></output> ~ <output name="a"></output>
</div>
<pre>
Changes:
1. allow different min/max/step for two inputs
2. new property 'maxGap'
3. added events 'input'/'change'
4. dropped IE/OldEdge support
</pre>
For those working with Vue, there is now Veeno available, based on noUiSlider. But it does not seem to be maintained anymore. :-(
This code covers following points
Dual slider using HTML, CSS, JS
I have modified this slider using embedded ruby so we can save previously applied values using params in rails.
<% left_width = params[:min].nil? ? 0 : ((params[:min].to_f/100000) * 100).to_i %>
<% left_value = params[:min].nil? ? '0' : params[:min] %>
<% right_width = params[:max].nil? ? 100 : ((params[:max].to_f/100000) * 100).to_i %>
<% right_value = params[:max].nil? ? '100000' : params[:max] %>
<div class="range-slider-outer">
<div slider id="slider-distance">
<div class="slider-inner">
<div inverse-left style="width:<%= left_width %>%;"></div>
<div inverse-right style="width:<%= 100 - right_width %>%;"></div>
<div range style="left:<%= left_width %>%;right:<%= 100 - right_width %>%;"></div>
<span thumb style="left:<%= left_width %>%;"></span>
<span thumb style="left:<%= right_width %>%;"></span>
<div sign style="">
Rs.<span id="value"><%= left_value.to_i %></span> to
</div>
<div sign style="">
Rs.<span id="value"><%= right_value.to_i %></span>
</div>
</div>
<input type="range" name="min" value=<%= left_value %> max="100000" min="0" step="100" oninput="
this.value=Math.min(this.value,this.parentNode.childNodes[5].value-1);
let value = (this.value/parseInt(this.max))*100
var children = this.parentNode.childNodes[1].childNodes;
children[1].style.width=value+'%';
children[5].style.left=value+'%';
children[7].style.left=value+'%';children[11].style.left=value+'%';
children[11].childNodes[1].innerHTML=this.value;" />
<input type="range" name="max" value=<%= right_value %> max="100000" min="0" step="100" oninput="
this.value=Math.max(this.value,this.parentNode.childNodes[3].value-(-1));
let value = (this.value/parseInt(this.max))*100
var children = this.parentNode.childNodes[1].childNodes;
children[3].style.width=(100-value)+'%';
children[5].style.right=(100-value)+'%';
children[9].style.left=value+'%';children[13].style.left=value+'%';
children[13].childNodes[1].innerHTML=this.value;" />
</div>
<div class="range-label">
<div>0</div>
<div>100000</div>
</div>
</div>
[slider] {
/*width: 300px;*/
position: relative;
height: 5px;
/*margin: 20px auto;*/
/* height: 100%; */
}
[slider] > div {
position: absolute;
left: 13px;
right: 15px;
height: 14px;
top: 5px;
}
[slider] > div > [inverse-left] {
position: absolute;
left: 0;
height: 14px;
border-radius: 3px;
background-color: #CCC;
/*margin: 0 7px;*/
margin: 0 -7px;
}
[slider] > div > [inverse-right] {
position: absolute;
right: 0;
height: 14px;
border-radius: 3px;
background-color: #CCC;
/*margin: 0 7px;*/
margin: 0 -7px;
}
[slider] > div > [range] {
position: absolute;
left: 0;
height: 14px;
border-radius: 14px;
background-color:#8950fc;
}
[slider] > div > [thumb] {
position: absolute;
top: -3px;
z-index: 2;
height: 20px;
width: 20px;
text-align: left;
margin-left: -11px;
cursor: pointer;
/* box-shadow: 0 3px 8px rgba(0, 0, 0, 0.4); */
background-color: #FFF;
/*border-radius: 50%;*/
border-radius:2px;
outline: none;
}
[slider] > input[type=range] {
position: absolute;
pointer-events: none;
-webkit-appearance: none;
z-index: 3;
height: 14px;
top: -2px;
width: 100%;
opacity: 0;
}
div[slider] > input[type=range]:focus::-webkit-slider-runnable-track {
background: transparent;
border: transparent;
}
div[slider] > input[type=range]:focus {
outline: none;
}
div[slider] > input[type=range]::-webkit-slider-thumb {
pointer-events: all;
width: 28px;
height: 28px;
border-radius: 0px;
border: 0 none;
background: red;
-webkit-appearance: none;
}
div[slider] > input[type=range]::-ms-fill-lower {
background: transparent;
border: 0 none;
}
div[slider] > input[type=range]::-ms-fill-upper {
background: transparent;
border: 0 none;
}
div[slider] > input[type=range]::-ms-tooltip {
display: none;
}
[slider] > div > [sign] {
/* opacity: 0;
position: absolute;
margin-left: -11px;
top: -39px;
z-index:3;
background-color:#1a243a;
color: #fff;
width: 28px;
height: 28px;
border-radius: 28px;
-webkit-border-radius: 28px;
align-items: center;
-webkit-justify-content: center;
justify-content: center;
text-align: center;*/
color: #A5B2CB;
border-radius: 28px;
justify-content: center;
text-align: center;
display: inline-block;
margin-top: 12px;
font-size: 14px;
font-weight: bold;
}
.slider-inner{
text-align:center;
}
/*[slider] > div > [sign]:after {
position: absolute;
content: '';
left: 0;
border-radius: 16px;
top: 19px;
border-left: 14px solid transparent;
border-right: 14px solid transparent;
border-top-width: 16px;
border-top-style: solid;
border-top-color:#1a243a;
}*/
[slider] > div > [sign] > span {
font-size: 12px;
font-weight: 700;
line-height: 28px;
}
[slider]:hover > div > [sign] {
opacity: 1;
}
.range-label{
display: flex;
justify-content: space-between;
margin-top: 28px;
padding: 0px 5px;
}
.range-slider-outer{
width:calc(100% - 20px);
margin:auto;
margin-bottom: 10px;
margin-top: 10px;
}

How can a span id that div classes are applied to be looped?

I'm new to coding, and I'm trying to learn the basics. I wanted to practice what I learned by making flashcards (nothing complicated like saving it, importing it, or exporting it). So far, I made a table that the user can edit. I know how to gather data from the table, but I don't know how to make a CSS flashcard appear every time the user adds a card to the table. I am aware that the code will not work since I put the CSS in JavaScript since this code is just meant to show what I am trying to do. Also, if I am taking a completely wrong approach, please let me know. Thank you! Please excuse the poor variable naming, I was just testing some things.
<script>
function getFlashcardValue() {
for (var repeat = 0; repeat < 200; repeat++) {
var Table = document.getElementById('flashcardsTable');
var column1 = 0;
var column2 = 1;
var numberOfFlashcards = 2;
for (var row = 0; row < numberOfFlashcards; row++) {
var Cells = Table.rows.item(1).cells;
var Question1 = Cells.item(column1).innerHTML;
var Cells1 = Table.rows.item(1).cells;
var Answer1 = Cells.item(column2).innerHTML;
document.getElementById("myFlashcardQuestion" + row).innerHTML = Question1;
document.getElementById("myFlashcardAnswer" + row).innerHTML = Answer1;
<div class="flip-card">
<div class="flip-card-inner">
<div class="flip-card-front">
<span id="myFlashcardQuestion1"></span>
</div>
<div class="flip-card-back">
<span id="myFlashcardAnswer1"></span>
</div>
</div>
</div>
}
}
}
</script>
<p style = "font-size: 25px">Hover over the flashcard to flip it!</p>
<style>
.flip-card {
background-color: transparent;
width: 350px;
height: 175px;
margin: auto;
padding: 5px 5px;
perspective: 1000px;
}
.flip-card-inner {
position: relative;
background-color: lightblue;
width: 100%;
height: 100%;
text-align: center;
transition: transform 0.6s;
transform-style: preserve-3d;
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
}
.flip-card:hover .flip-card-inner {
transform: rotateY(180deg);
}
.flip-card-front, .flip-card-back {
position: absolute;
width: 100%;
height: 100%;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
.flip-card-front {
background-color: lightblue;
width: 350px;
height: 175px;
color: black;
font-size: 35px;
text-alignment: center;
}
.flip-card-back {
background-color: red;
color: white;
font-size: 35px;
text-alignment: center;
transform: rotateY(180deg);
}
</style>
So first of all you can create a code snippet in stackoverflows editor (see below), or use jsfiddle and post a shared-link.
It depends on which action the user has to do after he enters the data.
If it is, for example, a button click, then it is possible to call a function that shows the user's input in the flashcard. Now if you want that for every single Q&A you have to create Elements in the for loop and edit them there. Here a little example.
var allCards = document.getElementById("allCards");
for (var i = 1; i <= 5; i++) { //i used 5, you should use length of data
var question = document.createElement("div");
question.textContent = "Question " + i;
question.classList.add("flip-card");
allCards.appendChild(question);
}
.flip-card {
background-color: lightblue;
width: 350px;
height: 175px;
margin: 10px auto;
padding: 5px 5px;
font-size: 35px;
text-align: center;
}
<div id="allCards"></div>
Edit:
As promised, here is an example of how you can set up the flip cards.
https://jsfiddle.net/ybu59hfp/1/
Your concern should now be resolved. If you have any further questions, feel free to write to me in the chat or read a little about JavaScript on the Internet.

Chaining button clicks with jQuery

I'm trying to build a simple website that let's you click 6 different buttons. Each click of the button is supposed to trigger the display of specific content (as an img file). Clicking each of the six buttons should lead to different content. I have managed to achieve this part via Javascript getElementById.
However, to add a bit more complexity, I want to implement sequential decision making. Meaning that clicking Button "1" and THEN clicking Button "2" (or 3-6 for that matter) should each lead to the display of other specific content. Likewise clicking Button "1", then "2" and then "1" again should also display specific content. My sequential decision making is supposed to be limited to only two buttons interacting until the end of the decision is reached. So essentially, something like 1 -> 2 -> 3 can not happen, but 3 -> 6 -> 3 can happen. I hope it's not too complicated what I'm trying to do.
Anyway, here's some code I wrote trying to achieve this, but I'm fairly sure that my toggle function is not the correct way to go about it as I'm essentially simply placing pictures above each other and there is no sequency to any of the decisions made. I think to achieve this, I would need to chain the clicks, but I'm completely lost as to how to achieve that. Any help is greatly appreciated.
a:link {
color: white;
text-decoration: none;
}
a:visited {
color: white;
text-decoration: none;
}
a:hover {
color: white;
text-decoration: none;
}
a:active {
color: white;
text-decoration: none;
}
a.pos:link {
color: black;
text-decoration: none;
}
a.pos:visited {
color: black;
text-decoration: none;
}
a.pos:hover {
color: white;
text-decoration: none;
}
a.pos:active {
color: black;
text-decoration: none;
}
a.button:link, a.button:visited {
margin: auto;
position: absolute;
top: 0px;
left: 0px;
background-color: yellowgreen;
width: 345px;
line-height: 20px;
height: 185px;
border: 2px solid;
border-color: white;
text-align: center;
border-radius: 100px;
font-family: open sans;
font-size: 9px;
color: black;
font-weight: 650;
color: white;
padding: 14px 25px;
text-align: center;
text-decoration: none;
display: inline-block;
}
a.button:hover, a.button:active {
background-color: yellowgreen;
}
body {margin:0;}
ul {
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #333;
position: fixed;
top: 0;
width: 100%;
}
li {
float: left;
}
li a {
display: block;
color: white;
text-align: center;
padding: 20px 30px;
font-family: open sans;
text-decoration: none;
}
li a:hover:not(.active) {
background-color: #111;
}
.active {
background-color: #4CAF50;
}
h1 {
color: whitesmoke;
font-family: open sans;
font-size: 300%;
}
.table {
margin: auto;
position: relative;
width: 450px;
top: -1350px;
border: 6px solid #333333;
border-radius: 250px;
background: #737373;
padding-top: 150px;
padding-right: 50px;
padding-left: 50px;
padding-bottom: 150px;
}
#quattro {
margin: auto;
position: absolute;
bottom: -25px;
right: 250px;
background-color: gold;
width: 50px;
line-height: 50px;
height: 50px;
border: 1px solid black;
text-align: center;
border-radius: 50px;
font-family: open sans;
font-size: 20px;
font-weight: 650;
}
#uno {
margin: auto;
position: absolute;
top: -25px;
right: 250px;
background-color: gold;
width: 50px;
line-height: 50px;
height: 50px;
border: 1px solid black;
text-align: center;
border-radius: 50px;
font-family: open sans;
font-size: 20px;
font-weight: 650;
}
#duo {
margin: auto;
position: absolute;
top: 25px;
right: 10px;
background-color: gold;
width: 50px;
line-height: 50px;
height: 50px;
border: 1px solid black;
text-align: center;
border-radius: 50px;
font-family: open sans;
font-size: 20px;
font-weight: 650;
}
#tres {
margin: auto;
position: absolute;
bottom: 25px;
right: 10px;
background-color: gold;
width: 50px;
line-height: 50px;
height: 50px;
border: 1px solid black;
text-align: center;
border-radius: 50px;
font-family: open sans;
font-size: 20px;
font-weight: 650;
}
#cinqo {
margin: auto;
position: absolute;
bottom: 25px;
left: 10px;
background-color: gold;
width: 50px;
line-height: 50px;
height: 50px;
border: 1px solid black;
text-align: center;
border-radius: 50px;
font-family: open sans;
font-size: 20px;
font-weight: 650;
}
#seis {
margin: auto;
position: absolute;
top: 25px;
left: 10px;
background-color: gold;
width: 50px;
line-height: 50px;
height: 50px;
border: 1px solid black;
text-align: center;
border-radius: 50px;
font-family: open sans;
font-size: 20px;
font-weight: 650;
}
.imgrange1 {
text-align: center;
color: white;
position: absolute;
top: 400px;
left: -400px;
}
.imgrange2 {
text-align: center;
color: white;
position: absolute;
top: 400px;
left: 320px;
}
.centered {
font-family: open sans;
font-size: 150%;
position: absolute;
top: -3%;
left: 50%;
transform: translate(-50%, -50%);
}
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" href="css/style.css">
<style>
.myimgdivtoggle1 {
display: none;
}
.myimgdivtoggle2 {
display: none;
}
.myimgdivtoggle3 {
display: none;
}
.myimgdivtoggle4 {
display: none;
}
.myimgdivtoggle5 {
display: none;
}
</style>
<script>
$(document).ready(function(){
$('.togglebtn1').click(function(){
$('.myimgdivtoggle1').toggle();
});
});
$(document).ready(function(){
$('.togglebtn2').click(function(){
$('.myimgdivtoggle2').toggle();
});
});
$(document).ready(function(){
$('.togglebtn3').click(function(){
$('.myimgdivtoggle3').toggle();
});
});
$(document).ready(function(){
$('.togglebtn4').click(function(){
$('.myimgdivtoggle4').toggle();
});
});
$(document).ready(function(){
$('.togglebtn5').click(function(){
$('.myimgdivtoggle5').toggle();
});
});
$(document).ready(function(){
$('.togglebtn6').click(function(){
$('.myimgdivtoggle6').toggle();
});
});
</script>
</head>
<body>
<ul>
<li><a class="active" href="index.html">Main</a></li>
<li>News</li>
<li>Contact</li>
<li>About</li>
</ul>
<div style="padding:20px;margin-top:30px;background-color:cadetblue;height:1500px;">
<h1><center>TEST</center></h1>
</div>
<div class="table">
<button type="button" class="togglebtn1" id="uno">1</button>
<div class="myimgdivtoggle1">
<img src="1.JPG" class="imgrange1"/>
</div>
<button type="button" class="togglebtn2" id="duo">2</button>
<div class="myimgdivtoggle2">
<img src="2.JPG" class="imgrange1"/>
</div>
<button type="button" class="togglebtn3" id="tres">3</button>
<div class="myimgdivtoggle3">
<img src="3.JPG" class="imgrange1"/>
</div>
<button type="button" class="togglebtn4" id="quattro">4</button>
<div class="myimgdivtoggle4">
<img src="4.JPG" class="imgrange1"/>
</div>
<button type="button" class="togglebtn5" id="cinqo">5</button>
<div class="myimgdivtoggle5">
<img src="5.JPG" class="imgrange1"/>
</div>
<button type="button" class="togglebtn6" id="seis">6</button>
<div class="myimgdivtoggle6">
<img src="6.JPG" class="imgrange1"/>
</div>
</body>
</html>
Though I can not provide you with a firm solution, I can however offer a small example which illustrates how to incorporate an array which tracks the buttons that have been clicked, as well as a way to get certain content from combinations of buttons.
Run the example and try the combinations 363, 254, 521 and 165 to get some results showing up. I've tried my best to show what the produced output is.
I'd suggest that you take a look at it and ask any questions if you have them. I'll check in to see if you do.
$(document).ready(function() {
/**
* Select the buttons.
* The $display and $clickedButtons are just to output
* the values that are stored.
*/
const $buttons = $('.button');
const $display = $('#display');
const $clickedButtons = $('#clicked-buttons');
const $removeButton = $('#remove-button');
/**
* Array which tracks your clicked buttons.
* If a button is clicked, the value of that button should be added to this array.
* The combination of the values will then later represent the key.
*/
const values = [];
/**
* This is where any know combinations are stored.
* The values in the values array will later be transformed into a single string to
* see if it matches any key in the combinations object below.
* If it does, it will give you a value, otherwise undefined.
*/
const combinations = {
"363": "https://www.fillmurray.com/200/200",
"254": "https://www.fillmurray.com/220/220",
"521": "https://www.fillmurray.com/240/240",
"165": "https://www.fillmurray.com/300/300"
};
/**
* Combines the values to form a single key and check if that key matches a combination.
* If there is a match the content should be anything other than undefined.
*/
function tryCombination() {
// This will output the current values from the array.
$clickedButtons.text(values);
// Transform the array into a single string.
// This will be the key to select content.
// ["1", "2", "3"] becomes "123".
const key = values.join('');
// Check if key has a match in the combinations object.
const url = combinations[key];
if (url !== undefined) {
// It does, show the content.
$display.attr('src', url);
$display.removeClass('hidden');
} else {
// It doesn't, empty the content.
$display.removeAttr('src');
$display.addClass('hidden');
}
}
/**
* Listen for the click event on all the buttons.
* When clicked, get the value of that clicked button and add that to the values array.
* It then calls the tryCombination function to evaluate if the values in the values
* array make a valid combination.
*/
$buttons.on('click', function() {
// This is the currently clicked button.
const $button = $(this);
// Get the value of the button.
const value = $button.val();
// If there already are 3 previously clicked buttons,
// then empty the array, so we can start a new combination.
if (values.length === 3) {
values.length = 0;
}
// Now add the newly clicked value.
values.push(value);
// Render and try the combination.
tryCombination();
});
/**
* Remove the last item in the values array.
* Then retry to create a valid combination.
*/
$removeButton.on('click', function() {
// Remove the last item from the values array
values.pop();
// Render and try the new combination.
tryCombination();
})
});
.container {
display: grid;
grid-template-rows: auto auto;
grid-template-columns: 200px 1fr;
grid-gap: 1em;
border: 1px solid #d0d0d0;
background-color: #f7f7f7;
padding: 1em;
border-radius: 5px;
}
.buttons {
grid-area: 1 / 1 / 2 / 3;
}
#display {
grid-area: 2 / 1 / 3 / 2;
width: 200px;
height: 200px;
background-color: #d0d0d0;
border-radius: 5px;
}
#clicked-buttons {
grid-area: 2 / 2 / 3 / 3;
display: block;
background-color: #d0d0d0;
border-radius: 5px;
padding: 1em;
margin: 0;
}
#remove-button {
grid-area: 1 / 2 / 2 / 3;
}
.hidden {
opacity: 0;
visibility: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="buttons">
<button class="button" id="1" value="1" >1</button>
<button class="button" id="2" value="2" >2</button>
<button class="button" id="3" value="3" >3</button>
<button class="button" id="4" value="4" >4</button>
<button class="button" id="5" value="5" >5</button>
<button class="button" id="6" value="6" >6</button>
</div>
<img id="display" class="hidden">
<button id="remove-button">Remove last input</button>
<code id="clicked-buttons"></code>
</div>
Edit
In the spirit of showing is better than telling; your last comment was about having a loose combination of numbers. This adds another layer of complexity.
Objects can only have keys that are strings (or Symbols) to get and set values. But in your case you'll want an array of numbers which represent the keys in any order, so plain objects are not suitable anymore.
The solution for this is the Map object. This object can have any type of key and value. So we can make a link between a combination of numbers and the images they represent (hence the name "map").
The example below uses this method. I've written a function that checks if an array of numbers is a match with any combination in the map. And if it does it return an array of images, referring to your previous comment.
Check it out. I believe this one to be more complex, so once more feel free to ask questions.
/**
* Create a Map instance.
*/
const combinations = new Map();
/**
* Values and keys are added with the set() method.
* This could still be improved with a loop setting each
* combination / images pair.
*/
combinations.set([3, 3, 6], ['https://www.fillmurray.com/200/200', 'https://www.fillmurray.com/200/200']);
combinations.set([2, 4, 5], ['https://www.fillmurray.com/220/220', 'https://www.fillmurray.com/220/220']);
combinations.set([1, 2, 5], ['https://www.fillmurray.com/240/240']);
combinations.set([1, 5, 6], ['https://www.fillmurray.com/300/300', 'https://www.fillmurray.com/300/300', 'https://www.fillmurray.com/300/300']);
const tryCombination = (key, combinations) => {
/**
* Loop over every combination.
* [combination, images] exposes the key-value pair,
* it's just a syntax to write fewer lines
*/
for (const [combination, images] of combinations) {
/**
* Create an array for the matches. If a number of the
* combination is in the given key, then that number
* will be pushed to the matches list. In the end,
* if everything matches, we should have just as many
* matches as numbers in the combination. That way
* we know if a key is correct.
*/
const matches = [];
/**
* We'll do some manipulation on the combination array,
* so to keep it intact we make a copy and manipulate that instead.
*/
const combinationCopy = Array.from(combination);
/**
* Count backwards through the combination array.
* Backwards counting is necessary when you remove items
* from the array while looping. I'd suggest you look
* into that subject.
*/
for (let i = combinationCopy.length - 1; i >= 0; i--) {
/**
* Get the current number we're looping over.
*/
const number = combinationCopy[i];
/**
* If that number is in the key array..
*/
if (key.includes(number)) {
/**
* ..then push that number to the matches array..
*/
matches.push(number);
/**
* ..and remove it from the copied combination array.
* We do this to prevent duplicate hits for cases
* where you have multiple occurrences of the same number,
* like [3, 3, 6]. When the first 3 hits, it will be removed.
* Then we have [3, 6] and we know we only need one more
* 3 and a 6.
*/
combinationCopy.splice(i, 1);
}
}
/**
* Now if every number has been matched correctly, then
* the amount of matches should be the same as the length
* of the combination. If that is the case, return the
* images. Otherwise, do nothing.
*/
if (matches.length === combination.length) {
return images;
}
}
/**
* If there are no matches, just return false, notifying the
* user that the combination is incorrect.
*/
return false;
};
console.log(tryCombination([5, 4, 2], combinations)); // Hit!
console.log(tryCombination([5, 1, 6], combinations)); // Hit!
console.log(tryCombination([2], combinations)); // Fail!
console.log(tryCombination([5, 4, 4], combinations)); // Fail!
console.log(tryCombination([3, 6, 3], combinations)); // Hit!

How to keep button disabled even if user refreshes the page? Javascript

I have this little app that generates numbers between 1 and 90. I do it as I'm practicing with my early JS skills.
It's written in italian so for better understanding, well the title doesn't matter that much, it just says "wanna try your luck?" bla-bla.
Then I set a timeout that disables the "Genera Numeri" button for 1 hour after it's clicked (I set it to 3 seconds for the post), and a message pops up ("You can use it once every hour") but if you refresh the page, of course, you can click it again.
I was wondering, how can I make it that even if I refresh, the countdown would be still going?
I read another similar question where they were talking about Mongo (which I have no clue what it is, I suppose back end (?) ), and they mentioned the use of cookies to do that, which in my inexperienced eyes, could make sense.
Anyways, HERE the Javascript
let button = document.querySelector('.button')
let clear = document.querySelector('.clear')
let message = document.querySelector('.message')
let first = document.querySelector('.first')
let second = document.querySelector('.second')
let third = document.querySelector('.third')
let fourth = document.querySelector('.fourth')
let fifth = document.querySelector('.fifth')
let sixth = document.querySelector('.sixth')
button.addEventListener('click', ()=> {
first.innerText = Math.floor(Math.random() * (90 - 1) + 1)
second.innerText = Math.floor(Math.random() * (90 - 1) + 1)
third.innerText = Math.floor(Math.random() * (90 - 1) + 1)
fourth.innerText = Math.floor(Math.random() * (90 - 1) + 1)
fifth.innerText = Math.floor(Math.random() * (90 - 1) + 1)
sixth.innerText = Math.floor(Math.random() * (90 - 1) + 1)
message.classList.add('show')
button.disabled = true
setTimeout(function(){
button.disabled = false
message.classList.remove('show')
}, 1000 * 3)
})
clear.addEventListener('click', ()=> {
first.innerText = ''
second.innerText = ''
third.innerText = ''
fourth.innerText = ''
fifth.innerText = ''
sixth.innerText = ''
})
And HERE the snippet
let button = document.querySelector('.button')
let clear = document.querySelector('.clear')
let message = document.querySelector('.message')
let first = document.querySelector('.first')
let second = document.querySelector('.second')
let third = document.querySelector('.third')
let fourth = document.querySelector('.fourth')
let fifth = document.querySelector('.fifth')
let sixth = document.querySelector('.sixth')
button.addEventListener('click', ()=> {
first.innerText = Math.floor(Math.random() * (90 - 1) + 1)
second.innerText = Math.floor(Math.random() * (90 - 1) + 1)
third.innerText = Math.floor(Math.random() * (90 - 1) + 1)
fourth.innerText = Math.floor(Math.random() * (90 - 1) + 1)
fifth.innerText = Math.floor(Math.random() * (90 - 1) + 1)
sixth.innerText = Math.floor(Math.random() * (90 - 1) + 1)
message.classList.add('show')
button.disabled = true
setTimeout(function(){
button.disabled = false
message.classList.remove('show')
}, 1000 * 3)
})
clear.addEventListener('click', ()=> {
first.innerText = ''
second.innerText = ''
third.innerText = ''
fourth.innerText = ''
fifth.innerText = ''
sixth.innerText = ''
})
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-image: url('conf.jpg');
background-size: cover;
background-repeat: no-repeat;
height: 100vh;
background-position-x: right;
}
.overlay {
width: 100vw;
height: 100vh;
background: rgba(0,0,0,.3);
}
.container {
width: 95vw;
height: 420px;
margin: auto;
display: flex;
flex-direction: column;
border: 1px solid black;
justify-content: space-around;
align-items: center;
text-align: center;
font-family: helvetica;
position: absolute;
top: 40vh;
left: 50vw;
transform: translate(-50%, -50%);
background: rgba(0,0,0,.75);
color: white;
border-radius: 15px;
box-shadow: 0 0 15px black;
padding-bottom: 1em;
}
.title {
height: 50%;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
text-align: center;
letter-spacing: .5px;
}
h1 {
font-family: verdana;
border-bottom: 1px solid white;
}
.tenta {
font-size: 1.25em;
}
.numbers {
display: flex;
width: 100%;
justify-content: space-around;
}
.number {
width: 50px;
height: 50px;
border-radius: 50%;
border: 1px solid white;
text-align: center;
vertical-align: center;
display: flex;
justify-content: center;
align-items: center;
color: white;
font-family: tahoma;
font-size: 1.2em;
background: black;
}
.button {
padding: .5em 1.5em;
font-size: 1.1em;
border: none;
background: white;
border-radius: 5px;
box-shadow: 0 0 10px black;
transition: .2s linear;
outline: none;
background: rgb(0,255,117)
}
.button:active {
transform: scale(.95);
}
.clear {
padding: .2em .5em;
position: absolute;
bottom: 20px;
right: 20px;
border: none;
background: white;
border-radius: 5px;
box-shadow: 0 0 10px black;
transition: .2s linear;
outline: none;
}
.clear:active {
transform: scale(.95);
}
p {
color: white;
text-align: center;
position: relative;
top: calc(450px + 20vw);
font-family: helvetica;
font-size: 1.1em;
margin: auto;
background: rgba(0,0,0,.45);
padding: .5em 1em;
display: none;
}
p.show {
display: block;
}
<div class="overlay">
<div class="container">
<div class="title">
<h1>Number Generator</h1>
<h3 class="nonsai">Non sai quali numeri giocare?</h3>
<h3 class="tenta">Tenta la fortuna con l'algoritmo di<br> Number Generator!</h3>
</div>
<div class="numbers">
<div class="first number"></div>
<div class="second number"></div>
<div class="third number"></div>
<div class="fourth number"></div>
<div class="fifth number"></div>
<div class="sixth number"></div>
</div>
<button class="button">Genera Numeri</button>
<button class="clear">Refresh</button>
</div>
<p class="message">Puoi usare Number Generator una volta ogni ora</p>
</div>
<script src="main.js"></script>
I didn't read all your code but from the explanation it seems like you have wrong approach in general if you want to restrict the user from clicking the button. I think you are aware of the client and server side computation.
So, first of all. Surely you can restrict button clicks on client side and even in a consistent way (saving it after refresh) but you should keep in mind that an advanced internet user will be able to hack it easily because you have just some standart options to achieve it. One of them is using cookies: When user clicks a button save a cookie with name butttonClicked or smth with value true and a lifetime of 1 hour. Its easy googlable how to do it. On page load always check if the cookies is set and attach attribute disabled to button. but cookies are visible and user can delete it himself.
To really restrict it you should make some checks on the server side so that even if user was able to send a request somehow, no matter the button was disabled or no on the front end, you should stop performing an action and send back an error message.
One way to do this would be using the browser's built-in capability to store data for a particular domain by utilizing the local storage. In general it can be used to store any kind of data but in your case it should hold the time your button gets 'reactivated'.
Let me outline what needs to be done:
at page load, start a timer which continuously checks the time stored in the local storage in a short interval of e.g. 1 second.
if there is no time stored yet, store the current time
if there is a time stored and it's in the future, disable the button
if there is a time stored and it's in the past, enable the button
If the user presses the button, update the value in the local storage and disable the button.
Here's an example. If you click on the button, it will disable it for 3 seconds:
<html>
<head>
</head>
<body>
<button id="button">click me</button>
</body>
<script type="text/javascript">
var myButton=document.getElementById("button");
function storeTime(theTime)
{
localStorage.setItem("time", theTime);
}
function check()
{
var now=new Date().getTime();
if(!localStorage.getItem("time"))
{
storeTime(now);
}
var storedTime=parseInt(localStorage.getItem("time"));
if(storedTime>now)
{
myButton.disabled=true;
}
else
{
myButton.disabled=false;
}
}
function buttonClicked()
{
var now=new Date().getTime() + 3000;
storeTime(now);
myButton.disabled=true;
}
setInterval(check, 1000);
check();
myButton.addEventListener("click", buttonClicked);
</script>
</html>

Can't get HTMLCollection length [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I have several list elements on my page that are created dynamically, and my goal now is to access all the <li> on my page using getElementsByTagName, but when I try to do it, console tells me that the length of my li array is 0.
I've read documentation and examples on getElementsByTagName use cases but didn't track the problem.
Here is my code:
window.addEventListener('DOMContentLoaded', function() {
let btn = document.querySelector('.sw-btn');
let content = document.querySelector('.content');
let filmsList = document.createElement('ul');
function getFilms() {
axios.get('https://swapi.co/api/films/').then(res => {
content.appendChild(filmsList);
for (var i = 0; i < res.data.results.length; i++) {
res.data.results.sort(function(a, b) {
let dateA = new Date(a.release_date),
dateB = new Date(b.release_date);
return dateA - dateB;
});
(function updateFilms() {
let addFilm = document.createElement('li');
filmsList.appendChild(addFilm);
let addFilmAnchor = document.createElement('a');
let addFilmId = document.createElement('p');
let addFilmCrawl = document.createElement('p');
let addFilmDirector = document.createElement('p');
let addFilmDate = document.createElement('p');
addFilmAnchor.textContent = res.data.results[i].title;
addFilmId.textContent = `Episode ID: ${res.data.results[i].episode_id}`;
addFilmCrawl.textContent = `Episode description: ${res.data.results[i].opening_crawl}`;
addFilmDirector.textContent = `Episode director: ${res.data.results[i].director}`;
addFilmDate.textContent = `Episode release date: ${res.data.results[i].release_date}`;
addFilm.append(addFilmAnchor, addFilmId, addFilmCrawl, addFilmDirector, addFilmDate);
})();
}
}).catch(err => {
console.log("An error occured");
})
let links = document.getElementsByTagName('li');
console.log(links.length);
};
btn.addEventListener('click', getFilms);
});
body {
max-height: 100vh;
padding: 0;
margin: 0;
font-family: Muli;
}
body::before {
background: url('https://upload.wikimedia.org/wikipedia/commons/thumb/6/6c/Star_Wars_Logo.svg/1200px-Star_Wars_Logo.svg.png') no-repeat center / cover;
background-size: cover;
content: "";
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -2;
opacity: 0.1;
}
h1 {
text-align: center;
color: #660d41;
font-size: 3em;
margin-top: 10px;
letter-spacing: 1px;
}
main {
display: flex;
align-items: center;
flex-direction: column;
}
.content {
max-width: 55%;
overflow-y: scroll;
max-height: 75vh;
}
ul {
list-style-type: none;
padding: 10px 20px;
}
li {
border-bottom: 1px solid orangered;
margin-bottom: 30px;
}
li:last-child {
border-bottom: none;
margin-bottom: 0;
}
a {
font-size: 1.7em;
color: #b907d9;
cursor: pointer;
margin-bottom: 10px;
}
p {
font-size: 1.2rem;
color: #0f063f;
margin: 10px 0;
}
button {
padding: .5em 1.5em;
border: none;
color: white;
transition: all 0.2s ease-in;
background: #da2417;
border-radius: 20px;
font-size: 1em;
cursor: pointer;
margin-top: 15px;
}
button:focus {
outline: none;
}
button:hover {
background: #e7736b;
}
button:active {
box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.7) inset;
}
<link href="https://fonts.googleapis.com/css?family=Muli&display=swap" rel="stylesheet">
<h1>Star wars films</h1>
<main>
<div class="content"></div>
<button class="sw-btn">Find Films</button>
</main>
Here is the link on working pen. Thank you for your help.
So what's happening here is a classic mistake caused by an async ajax request. You are requesting data and then inserting elements based on that data. Then afterwards, you want to count elements.
The problem is though, that axios.get returns a Promise that will not immediately resolve, because a HTTP request is being made to another server. That obviously takes some time, hence the then method of the Promise interface.
What you want to do is move your counting code at the end of the .then() method, so that the elements are counted after they have been inserted.
tl;dr you are trying to count elements that simply aren't there at the time of the counting.
That's because length is outside of then.
That means you are logging length of empty html collection at the first rendering of the page.
put it inside of then and then it will show correct value
like this:
addFilm.append(addFilmAnchor, addFilmId, addFilmCrawl, addFilmDirector, addFilmDate);
let links = document.getElementsByTagName('li');
console.log(links.length);

Categories

Resources