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;
}
I have a Div slider that rotates a set of Divs in and out of focus. Everything was working fine until I tried switching everything to (table / table-cell) in order to keep them all the Divs the same height in CSS. Now they still rotate out but one div remains visible with a reduced width off to the side of the stage. I get a sense that its position related but just can't figure out what's causing the issue.
Affected Page - https://www.harpercollege.edu/dev/blog-slider-test.php
JS Code:
$('.blog-posts').wrapInner('<div class="blog-posts-stage" />');
// Calculate Conditions & Set Vars
// var playTimer = 9,
slideQty = $('.well').length,
slideWidth = $('.well').width(),
stageWidth = $('.blog-posts-stage').width(),
contWidth = $('.blog-posts').width();
if ((slideQty * slideWidth) < contWidth) {
$('.blog-posts-prev').addClass('blog-posts-prev-disabled').removeClass('blog-posts-prev');
$('.blog-posts-next').addClass('blog-posts-next-disabled').removeClass('blog-posts-next');
} else {
$('.blog-posts-prev-disabled').addClass('blog-posts-prev').removeClass('blog-posts-prev-disabled');
$('.blog-posts-next-disabled').addClass('blog-posts-next').removeClass('blog-posts-next-disabled');
}
$(window).resize(function() {
var slideQty = $('.well').length,
slideWidth = $('.well').width(),
stageWidth = $('.blog-posts-stage').width(),
contWidth = $('.blog-posts').width();
if ((slideQty * slideWidth) < contWidth) {
$('.blog-posts-prev').addClass('blog-posts-prev-disabled').removeClass('blog-posts-prev');
$('.blog-posts-next').addClass('blog-posts-next-disabled').removeClass('blog-posts-next');
} else {
$('.blog-posts-prev-disabled').addClass('blog-posts-prev').removeClass('blog-posts-prev-disabled');
$('.blog-posts-next-disabled').addClass('blog-posts-next').removeClass('blog-posts-next-disabled');
}
});
$('.blog-posts-next').on('click', function(event) {
event.preventDefault();
$('.blog-posts-stage').animate({
left: -(slideWidth)
}, 500, function() {
$('.well:first').appendTo('.blog-posts-stage');
$('.blog-posts-stage').css({
left: '0px'
});
});
});
$('.blog-posts-prev').on('click', function(event) {
event.preventDefault();
$('.well:last').prependTo('.blog-posts-stage');
$('.blog-posts-stage').css({
left: -(slideWidth)
});
$('.blog-posts-stage').animate({
left: '0px'
}, 500, function() {});
});
function moveForward() {
$('.blog-posts-stage').animate({
left: -(slideWidth)
}, 500, function() {
$('.well:first').appendTo('.blog-posts-stage');
$('.blog-posts-stage').css({
left: '0px'
});
});
}
var timer = setInterval(moveForward, playTimer);
$('.blog-posts, .blog-posts-prev, .blog-posts-next').hover(function(ev) {
// clearInterval(timer);
}, function(ev) {
// timer = setInterval( moveForward, playTimer);
});
CSS Code:
<style>
.blog-posts {
width: 100%;
background: #eee;
font-size: 0;
position: relative;
}
.blog-posts-prev,
.blog-posts-next {
display: inline-block;
background: #eee;
color: #000;
text-decoration: none;
padding: 10px;
margin: 5px 0;
}
.blog-posts-prev:hover,
.blog-posts-next:hover {
background: #ccc;
}
.blog-posts-prev-disabled,
.blog-posts-next-disabled {
display: inline-block;
background: #eee;
color: #ccc;
text-decoration: none;
padding: 10px;
margin: 5px 0;
}
.blog-posts-stage {
position: relative;
white-space: normal;
width: 100%;
height: 100%;
float: none;
}
.well {
background: #ccc;
box-shadow: inset -1px 0px 0px 0px rgb(255, 255, 255);
width: 100%;
font-size: 12px;
text-align: left;
display: table-cell;
height: 100%;
width: 100%;
}
.well .row .col-sm-12.col-md-12 h2 {
float: left;
margin-top: 0px;
}
</style>
You could just use a lightbox library and save yourself the effort, but if you really want to do this why not try flex?
.blog-posts-stage {
display: flex;
flex-direction: row;
overflow: hidden;
}
.well-large {
flex-shrink: 0;
}
I'm trying to make am image scroll that uses control button and also slides automatically, my problem now is I need to know how to return the div to starting point when the div hit end on left side or on right side, what I could get so far is to know when the div hit an end but don't know how to return it to starting point in case of automatic sliding.
var n = 3; //total number of slides to be display at once
var totalDeal = 9; //total deals to be display
var sliderTotalWidth = 100; //in percentage append % later
var z = (100 / totalDeal) + '%'; //ddslides width
var x = totalDeal / n;
var counter = 1;
$(".dialyDealSlider").css('width', x + '00%');
//$(".ddslides").css('width', ddslidesWidth);
fillDailyDeal(totalDeal, z);
function fillDailyDeal(totalDeal, z) {
var imgUrl = '{$img}';
var ext = '.jpg';
var width = "width:" + z
for (var i = 1; i <= totalDeal; i++) {
var myDiv = $(
"<div class='ddslides' style='" + width + "' >" +
'<div class="dddesc">' +
'<div class="ddtitle">' +
'The is the title of the item it\'s a long title' +
'</div>' +
"<div class='ddprice'>$"+i+"00</div>" +
'<button class="ddview" title="View Item">view item</button>' +
'</div>' +
'<div class="ddimg">' +
'</div>' +
'<div class="vDivider"></div>' +
'<div class="ddSaleBadge">sold 44%</div>' +
'</div>');
$("#dialyDealSlider").append(myDiv);
}
}
$("#prev, #next").click(function() {
if (this.id == "next") {
counter++;
} else {
counter--;
}
//console.log(counter);
if (counter == x) {
alert('rigth end');
//$("#dialyDealSlider").css('left', '0px');
}
if (counter == 1) {
alert('left end');
}
var ddslidesWidth = $(".ddslides").width();
var dir = this.id == "next" ? '-=' : '+=';
var width = ddslidesWidth * n;
//alert(leftpos);
$("#dialyDealSlider").animate({
left: dir + width
}, 800);
});
my code is here JSFIDDLE: https://jsfiddle.net/sammyzeal/LqpL1n2g/
As we can see in the above fiddle, when we slide the div and the price is between $700 and $900 we hit an end then there's an alert, on this point if a user keeps on clicking this next button how do I scroll to the starting point which is between price $100 and $300, I hope my question is cleared and thanks in advance for any help
I just saw that you updated the code as i said , good work. So i wont hesitate to help now, you should make a separate slideNow() function that slides next or previous and you should increment/decrement the counter after checking the current or previous slides, if it is the last slide create a function separate to goToStart() to move to the first slide and if it is th first slide just return from there and do nothing, you can use the following script and test it with various combinations like, first going to the last slide by clicking and then on the last slide click next and see if it goes to the first on and then try clicking left from the first slide and verify if it goes left or stays still.
See a demo below
var n = 3; //total number of slides to be display at once
var totalDeal = 9; //total deals to be display
var sliderTotalWidth = 100; //in percentage append % later
var z = (100 / totalDeal) + '%'; //ddslides width
var x = totalDeal / n;
var counter = 1;
$(".dialyDealSlider").css('width', x + '00%');
//$(".ddslides").css('width', ddslidesWidth);
fillDailyDeal(totalDeal, z);
function fillDailyDeal(totalDeal, z) {
var imgUrl = '{$img}';
var ext = '.jpg';
var width = "width:" + z
for (var i = 1; i <= totalDeal; i++) {
var myDiv = $(
"<div class='ddslides' style='" + width + "' >" +
'<div class="dddesc">' +
'<div class="ddtitle">' +
'The is the title of the item it\'s a long title' +
'</div>' +
"<div class='ddprice'>$" + i + "00</div>" +
'<button class="ddview" title="View Item">view item</button>' +
'</div>' +
'<div class="ddimg">' +
'</div>' +
'<div class="vDivider"></div>' +
'<div class="ddSaleBadge">sold 44%</div>' +
'</div>');
$("#dialyDealSlider").append(myDiv);
}
}
$("#prev, #next").click(function() {
var target = this.id;
//console.log(counter);
if (counter == x && target == "next") {
counter = 1;
goToStart();
return;
}
if (counter == 1 && target == "prev") {
return;
}
if (target == "next") {
counter++;
} else {
counter--;
}
//slide the slides
slideNow(target, n);
});
function goToStart() {
$("#dialyDealSlider").animate({
left: 0
}, 800);
}
function slideNow(target, n) {
var ddslidesWidth = $(".ddslides").width();
var dir = target == "next" ? '-=' : '+=';
var width = ddslidesWidth * n;
//alert(leftpos);
$("#dialyDealSlider").animate({
left: dir + width
}, 800);
}
.dailyDeal {
height: 150px;
width: 100%;
margin-top: 15px;
padding: 0 !important;
}
.dialyDealBox {
float: left;
height: 150px;
padding: 0 !important;
overflow: hidden;
}
.dialyDealSlider {
width: 100%;
height: 150px;
position: absolute;
border: 1px solid #ddd;
}
.ddslides {
height: 150px;
width: 2%;
float: left;
background-color: #fff;
position: relative;
padding: 10px 0;
}
#media only screen and (max-width: 768px) {
.ddslides {
width: 16.66%;
}
}
#media (max-width:320px) {
.ddslides {
width: 33.33%;
}
}
.ddimg {
float: left;
margin: auto;
height: 100%;
width: 32%;
margin-right: 4px;
}
.ddimg>a>img {
height: 100%;
width: 100%;
}
.dddesc {
float: left;
margin: auto;
height: 100%;
width: 65%;
padding: 0 10px 0 15px;
}
.ddSaleBadge {
position: absolute;
top: 5px;
right: 5px;
width: 37px;
height: 38px;
padding: 5px 2px 4px 3px;
overflow: hidden;
background: url("/trobay/img/icon/label.png") no-repeat 50% 50%;
line-height: 14px;
text-align: center;
color: #fff;
}
.ddtitle {
margin-bottom: 10px;
}
.ddtitle>a {
font-size: 14px;
color: #666666;
text-decoration: none;
}
.ddtitle>a:hover {
color: #e4393c;
text-decoration: none;
}
.ddprice {
color: #e4393c;
font-size: 16px;
margin-bottom: 10px;
}
.ddview {
color: #fff;
background-color: #e4393c;
font-size: 14px;
border: 0;
outline: 0;
padding: 4px;
font-weight: bold;
}
.ddslider-prev {
cursor: pointer;
position: absolute;
top: 50%;
width: auto;
margin-top: -22px;
padding: 5px;
color: #666666;
font-weight: bold;
font-size: 18px;
transition: 0.6s ease;
left: 0;
z-index: 10;
background-color: rgba(221, 221, 221, 1);
display: block;
}
.ddslider-next {
cursor: pointer;
position: absolute;
right: 0;
top: 50%;
width: auto;
margin-top: -22px;
padding: 5px;
color: #666666;
font-weight: bold;
font-size: 18px;
transition: 0.6s ease;
z-index: 10;
background-color: rgba(221, 221, 221, 1);
display: block;
}
.vDivider {
height: 130px;
border-right: 1px dotted #666;
position: absolute;
top: 10px;
;
right: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="row">
<div class="dailyDeal col-md-12">
<div class="dialyDealBox col-xs-10" id="dialyDealBox">
<div class="dialyDealSlider" id="dialyDealSlider">
</div>
<div class="ddslider-prev" id="prev">❮</div>
<div class="ddslider-next" id="next">❯</div>
</div>
</div>
</div>
I want to save and restore data from div - that is container of other divs,
In order to make it I use local storage and JSON like this:
window.onload = restoreJason;
function makeJson(){
var canvas = document.getElementById("canvas");
var shapes = canvas.querySelectorAll("div[class='drag']");
var divs = new Object();
for(var i=0; i<shapes.length; ++i){
divs[shapes[i].getAttribute('innerHTML')] = shapes[i].innerHTML;
}
localStorage.setItem("divs", JSON.stringify(divs));
}
function restoreJason(){
var divs = JSON.parse(localStorage.getItem("divs"));
var canvas = document.getElementById("canvas");
var shapes = canvas.querySelectorAll("div[class='drag']");
for(var i = 0; i<shapes.length; i++){
shapes[i].value = divs[shapes[i].getAttribute("innerHTML")];
}
console.log(divs);
}
However, I don't know how to access the innerHTML of the elements and save it or restore it.
What do you think I shall do?
(To be more detailed - I need to save the content of the div when user click on "save", and load it when the user click "load". This is a snippest of it...)
NOTICE: the "canvas" is just the id of the main div, and not a real "canvas".
function randomizeColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * letters.length)];
}
return color;
}
function randomizeRectangle() {
var width = Math.random() * 700 + 50;
var height = Math.random() * 250 + 50;
if (height <= 20) {
height = 20;
}
var posX = Math.round(Math.random() * window.innerWidth);
var posY = Math.round(Math.random() * window.innerHeight);
var randomRecObj = {
"width": width + "px",
"height": height + "px",
"posX": posX,
"posY": posY
};
return randomRecObj;
}
function makeShape() {
var rect = randomizeRectangle();
var rectDOM = document.createElement("div");
rectDOM.className = "rectangle";
rectDOM.style.border = "1px solid black";
rectDOM.style.width = rect.width;
rectDOM.style.height = rect.height;
rectDOM.style.background = randomizeColor();
rectDOM.style.top = rect.posY + "px";
rectDOM.style.left = rect.posX + "px";
//rectDOM.addEventListener("click", selectShape);
// rectDOM.style.transform =rect.rotation;
return rectDOM;
}
function randRect() {
var rectDOM = makeShape();
var canvas = document.getElementById("canvas");
canvas.appendChild(rectDOM);
}
function randOval() {
var ovalDOM = makeShape();
ovalDOM.style.borderRadius = "50%";
var canvas = document.getElementById("canvas");
canvas.appendChild(ovalDOM);
}
function changeColor(){
}
function cancelSelection() {
var selected = document.getElementsByClassName("selected");
while (selected) {
selected[0].classList.remove(selected[0]);
}
}
function removeShape(event) {
var selectedShapes = document.getElementsByClassName("selected");
var len = selectedShapes.length;
while (len > 0) {
selectedShapes[0].parentNode.removeChild(selectedShapes[0]);
--len;
}
}
function removeCorners(rectDom) {
var corners = document.getElementsByClassName("corners");
var len = corners.length;
while (len > 0) {
corners[0].classList.remove("corners");
--len;
}
}
.header{
background: #4ABDAC;
color: #fff;
margin: 1px;
}
hr{
border-top: 3px double #2a3132;
}
ul{
list-style: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #90afc5;
}
li{
float: left;
border: 2px solid #336b87;
padding: 3px;
margin: 3px;
}
li>a{
display: block;
color: #2a3132;
text-decoration: none;
padding: 10px 14px;
}
#color{
margin-left: 150px;
}
#rect{
margin-left: 100px;
}
li>a:hover{
color: #763626;
cursor: pointer;
}
#canvas{
background: #66a5ad;
position: relative;
height: 1200px;
width: 100%;
}
.corners{
position: absolute;
height: 10px;
width: 10px;
background:#fff;
border: 1px solid black;
display: none;
}
.selected .corners{
display: inline-block;
}
.cornerUpLeft{
top: -5px;
left: -5px;
}
.cornerUpRight{
top: -5px;
right: -5px;
}
.cornerBtmLeft{
bottom: -5px;
left: -5px;
}
.cornerBtmRight{
bottom: -5px;
right: -5px;
}
.rectangle{
position: absolute;
}
.colorBox{
border: 1px solid black;
height: 20px;
width: 20px;
list-style: none;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Sketch Board - Eyal Segal Project</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="css/style.css" rel="stylesheet">
<script src="js/sketch.js"></script>
</head>
<body>
<h1 class="header">Sketch Board</h1>
<div>
<ul class="toolbar">
<li><a>Load</a></li>
<li id="Save"><a>Save</a></li>
<li id="rect"><a onclick="randRect()">Rectangle</a></li>
<li><a onclick="randOval()">Oval</a></li>
</ul>
<hr>
</div>
<div class="canvas" id="canvas">
</div>
</body>
</html>
All you need to do is set the .innerHTML of the div id="canvas" into localStorage. There's no need for JSON or loops at all.
Also, don't use inline HTML event attributes (onclick). Instead, do all your JavaScript separately using modern, standards based event handling.
Lastly, there is no need for <a> elements to be able to respond to a click event. Actually, your a elements are invalid as they don't have a name or href attribute anyway. The li elements can simply be set up for click events.
This is the code to do it but it won't execute here in the Stack Overflow snippet environment, but you can see it working here.
// Get reference to the "canvas"
var can = document.getElementById("canvas");
// Save the content of the canvas to localStorage
function saveData(){
localStorage.setItem("canvas", can.innerHTML);
}
// Get localStorage data
function restoreData(){
can.innerHTML = localStorage.getItem("canvas");
}
// get load and save references
var load = document.getElementById("load");
var save = document.getElementById("save");
// Set up event handlers
load.addEventListener("click", restoreData);
save.addEventListener("click", saveData);
// Get references to the rect and oval "buttons" and set their event handlers
var rect = document.getElementById("rect");
rect.addEventListener("click", randRect);
var oval = document.getElementById("oval");
oval.addEventListener("click", randOval);
function randomizeColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * letters.length)];
}
return color;
}
function randomizeRectangle() {
var width = Math.random() * 700 + 50;
var height = Math.random() * 250 + 50;
if (height <= 20) {
height = 20;
}
var posX = Math.round(Math.random() * window.innerWidth);
var posY = Math.round(Math.random() * window.innerHeight);
var randomRecObj = {
"width": width + "px",
"height": height + "px",
"posX": posX,
"posY": posY
};
return randomRecObj;
}
function makeShape() {
var rect = randomizeRectangle();
var rectDOM = document.createElement("div");
rectDOM.className = "rectangle";
rectDOM.style.border = "1px solid black";
rectDOM.style.width = rect.width;
rectDOM.style.height = rect.height;
rectDOM.style.background = randomizeColor();
rectDOM.style.top = rect.posY + "px";
rectDOM.style.left = rect.posX + "px";
//rectDOM.addEventListener("click", selectShape);
// rectDOM.style.transform =rect.rotation;
return rectDOM;
}
function randRect() {
var rectDOM = makeShape();
var canvas = document.getElementById("canvas");
canvas.appendChild(rectDOM);
}
function randOval() {
var ovalDOM = makeShape();
ovalDOM.style.borderRadius = "50%";
var canvas = document.getElementById("canvas");
canvas.appendChild(ovalDOM);
}
function changeColor(){
}
function cancelSelection() {
var selected = document.getElementsByClassName("selected");
while (selected) {
selected[0].classList.remove(selected[0]);
}
}
function removeShape(event) {
var selectedShapes = document.getElementsByClassName("selected");
var len = selectedShapes.length;
while (len > 0) {
selectedShapes[0].parentNode.removeChild(selectedShapes[0]);
--len;
}
}
function removeCorners(rectDom) {
var corners = document.getElementsByClassName("corners");
var len = corners.length;
while (len > 0) {
corners[0].classList.remove("corners");
--len;
}
}
.header{
background: #4ABDAC;
color: #fff;
margin: 1px;
}
hr{
border-top: 3px double #2a3132;
}
ul{
list-style: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #90afc5;
}
li{
float: left;
border: 2px solid #336b87;
padding: 3px;
margin: 3px;
}
li>a{
display: block;
color: #2a3132;
text-decoration: none;
padding: 10px 14px;
}
#color{
margin-left: 150px;
}
#rect{
margin-left: 100px;
}
li>a:hover{
color: #763626;
cursor: pointer;
}
#canvas{
background: #66a5ad;
position: relative;
height: 1200px;
width: 100%;
}
.corners{
position: absolute;
height: 10px;
width: 10px;
background:#fff;
border: 1px solid black;
display: none;
}
.selected .corners{
display: inline-block;
}
.cornerUpLeft{
top: -5px;
left: -5px;
}
.cornerUpRight{
top: -5px;
right: -5px;
}
.cornerBtmLeft{
bottom: -5px;
left: -5px;
}
.cornerBtmRight{
bottom: -5px;
right: -5px;
}
.rectangle{
position: absolute;
}
.colorBox{
border: 1px solid black;
height: 20px;
width: 20px;
list-style: none;
}
<h1 class="header">Sketch Board</h1>
<div>
<ul class="toolbar">
<li id="load">Load</li>
<li id="save">Save</li>
<li id="rect">Rectangle</li>
<li id="oval">Oval</li>
</ul>
<hr>
</div>
<div class="canvas" id="canvas">
</div>
I have absolutely positioned elements with different position.top and height generated from database.
All I'm trying to do is to un-collide these elements by shifting them to the right while adjusting width to fit inside the <body> container.
I'm having an issue applying 'left' position to the collided elements.
I use https://sourceforge.net/projects/jquerycollision/ to detect collision.
Here is how the final picture should look:
$('div').each(function() {
var name = $(this).text();
var hits = $(this).collision('div').not(this); // Find colliding elements
console.log(name + ' collides with: ' + hits.length + ' others');
if (hits.length > 0) {
var widthAll = 100 / (hits.length + 1);
// Shift colliding elements to the right with equal width
$(hits).add(this).each(function(i) {
var name = $(this).text();
$(this).css({ 'left': widthAll * i + '%', 'width': widthAll + '%' });
});
}
});
div {
position: absolute;
width: 10em;
font-size: 0.75em;
color: white;
}
.blue {
top: 0;
height: 80%;
background-color: blue;
}
.red {
top: 15%;
height: 5%;
background-color: red;
}
.yellow {
top: 17%;
height: 10%;
background-color: yellow;
color: black;
}
.green {
top: 30%;
height: 5%;
background-color: green;
}
.magenta {
top: 36%;
height: 3%;
background-color: magenta;
}
.cyan {
top: 50%;
height: 5%;
background-color: cyan;
color: black;
}
.brown {
top: 81%;
height: 5%;
background-color: brown;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://rawgit.com/dsbaars/jquery-collision/master/js/jquery-collision.min.js"></script>
<div class='blue'>blue</div>
<div class='red'>red</div>
<div class='yellow'>yellow</div>
<div class='green'>green</div>
<div class='magenta'>magenta</div>
<div class='cyan'>cyan</div>
<div class='brown'>brown</div>
I think I have completed your code as you have requested. The idea is,
First block of code shifts the divs to the right so that they don't overlap.
Second block makes the width of the divs evenly distributed according to size of the body.
Last block increases the width of rest of the divs to take remaining space.
"use strict";
var divs = $('div'),
mx = 0,
mxs = [0],
bw = $("body").outerWidth(),
steps = 1;
divs.each(function(i) {
for (var j = i + 1; j < divs.length; j++) {
if (!$(this).data("x")) $(this).data("x", 0);
if (j < divs.length) {
var hit = $(this).collision(divs[j]);
if (hit.length) {
hit = $(divs[j]);
hit.css("left", "+=" + Math.ceil($(this).outerWidth()));
hit.data("x", hit.position().left);
if (mx < hit.data("x")) {
mxs.push(mx = hit.data("x"));
steps++;
}
}
}
}
});
divs.each(function(i) {
let iw = $(this).outerWidth(),
fw = bw / steps;
$(this).outerWidth(fw);
for (var j = i + 1; j < divs.length; j++) {
$(this).collision(divs[j]).css("left", "+=" + Math.ceil((fw - iw) * mxs.indexOf($(divs[j]).data("x"))));
}
});
divs.each(function() {
var os = $(this).outerWidth(),
ts = bw - $(this).position().left;
$(this).outerWidth(ts);
if ($(this).collision(divs).not(this).length) {
$(this).outerWidth(os);
}
});
body {
margin: 0;
}
div {
position: absolute;
width: 10em;
font-size: 0.75em;
color: white;
left: 0;
}
.blue {
top: 0;
height: 80%;
background-color: blue;
}
.red {
top: 15%;
height: 5%;
background-color: red;
}
.yellow {
top: 17%;
height: 10%;
background-color: yellow;
color: black;
}
.green {
top: 20%;
height: 50%;
background-color: green;
}
.magenta {
top: 36%;
height: 3%;
background-color: magenta;
}
.cyan {
top: 50%;
height: 5%;
background-color: cyan;
color: black;
}
.brown {
top: 81%;
height: 5%;
background-color: brown;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rawgit.com/dsbaars/jquery-collision/master/js/jquery-collision.min.js"></script>
<div class='blue'>blue</div>
<div class='red'>red</div>
<div class='yellow'>yellow</div>
<div class='green'>green</div>
<div class='magenta'>magenta</div>
<div class='cyan'>cyan</div>
<div class='brown'>brown</div>
Above snippet is non-responsive. If you wish it to be responsive, then simply listen to resize event, change the value of bw and repeat code blocks 2 and 3.
As mentioned in the comments: jquery-collision.min.js had some unressolved bugs so, as suggested by Alex G, https://www.48design.de/de/news/2009/11/20/kollisionsabfrage-per-jquery-plugin-update-v11/ may be an alternative.