Beispiel:Min-Max-Schieberegler.html

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

<!DOCTYPE html> <html lang="de"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Min-Max-Schieberegler</title> <style> output { display: block; } button { font-size: 1em; } double-range-input { border: thin dotted black; margin: 1em; width: 20em; height: 3em; display: block; } double-range-input[name=dr2] { --trackInnerColor: green; --trackOuterColor: lightgreen; --thumbColor: red; --thumbFocusColor: yellow; --thumbSize: 1.2em; --trackHeight: 1em; box-sizing: border-box; background-color: darkgray; border-radius: .5em; padding: .5em; } double-range-input[name=dr4]::part(valuedisplay) { border: thin solid blue; padding: .1em; } </style>

<script> 'use strict';

// Custom-Element double-range-input anlegen customElements.define('double-range-input', class extends HTMLElement {

// Einige private Eigenschaften #style; #doubleSlider; #inputLow; #inputHigh; #valueDisplay; #Attributes; #internals;

// double-range-input soll Formularelement sein static formAssociated = true;

// Festlegen, welche Attribute überwacht werden sollen static get observedAttributes() { return ['min', 'max', 'step', 'showvalues', 'value']; }

constructor() { super();

// Zugriff auf die ElementInternals, wird benötigt, um auf die Werte zuzugreifen, die mit dem Formular versendet werden. this.#internals = this.attachInternals();

// Aria-Rolle slider setzen this.#internals.ariaRole = 'slider';

// Schatten-DOM anlegen const shadow = this.attachShadow({mode: 'closed'}); this.#style = document.createElement('style'); shadow.appendChild(this.#style); this.#doubleSlider = document.createElement('div'); this.#doubleSlider.id = 'wrapper'; this.#doubleSlider.part = 'doubleslider'; shadow.appendChild(this.#doubleSlider);

// Defaultwerte der Attribute setzen this.#Attributes = { min: 0, max: 100, step: 1, showvalues: 'ever', currentValue: '25,75' }

// Eventhandler this.#doubleSlider.addEventListener('input', this.#handleInput.bind(this)); this.#doubleSlider.addEventListener('pointerdown', this.#handlePointerdown.bind(this)); this.#doubleSlider.addEventListener('pointerover', this.#handlePointerover.bind(this)); this.#doubleSlider.addEventListener('pointerout', this.#handlePointerout.bind(this)); this.#doubleSlider.addEventListener('change', this.#handleChange.bind(this)); }

connectedCallback() { // CSS und Elemente anlegen this.#style.innerHTML = this.#css(); this.#doubleSlider.innerHTML = this.#html(); this.#inputLow = this.#doubleSlider.querySelector('#Low'); this.#inputHigh = this.#doubleSlider.querySelector('#High'); this.#valueDisplay = this.#doubleSlider.querySelector('output');

this.#update(); }

attributeChangedCallback(name, oldValue, newValue) { switch(name) { case 'min': this.#Attributes.min = Number(newValue); if (this.#inputLow) this.#inputLow.min = Number(newValue); if (this.#inputHigh) this.#inputHigh.min = Number(newValue); break; case 'max': this.#Attributes.max = Number(newValue); if (this.#inputLow) this.#inputLow.max = Number(newValue); if (this.#inputHigh) this.#inputHigh.max = Number(newValue); break; case 'step': this.#Attributes.step = Number(newValue); if (this.#inputLow) this.#inputLow.step = Number(newValue); if (this.#inputHigh) this.#inputHigh.step = Number(newValue); break; case 'value': this.#setValue(newValue); break; case 'showvalues': if(['ever', 'active', 'never'].includes(newValue)) this.#Attributes.showvalues = newValue; break; } }

get form() { return this.#internals.form; }

set name(name) { this.setAttribute('name', name); }

get name() { return this.getAttribute('name'); }

set min(min) { this.setAttribute('min', min); }

get min() { return this.#Attributes.min; }

set max(max) { this.setAttribute('max', max); } get max() { return this.#Attributes.max; }

set step(step) { this.setAttribute('step', step); }

get step() { return this.#Attributes.step; }

set showvalues(showvalues) { this.setAttribute('showvalues', showvalues); }

get showvalues() { return this.#Attributes.showvalues; }

set value(value) { this.#setValue(value); }

get value() { const valueLow = this.#inputLow.value; const valueHigh = this.#inputHigh.value; const currentValue = `${valueLow},${valueHigh}`; return currentValue; }

#setValue(value) { if(value.split(',').length == 2) { let valueLow = Number(value.split(',')[0]); let valueHigh = Number(value.split(',')[1]); if ( valueLow > valueHigh ) { let temp = valueLow; valueLow = valueHigh; valueHigh = temp; } valueLow =this.#round(valueLow,this.#Attributes.step); valueHigh = this.#round(valueHigh,this.#Attributes.step); this.#Attributes.currentValue = `${valueLow},${valueHigh}`; this.#internals.setFormValue(this.#Attributes.currentValue); if (this.#valueDisplay) this.#valueDisplay.innerHTML = `${valueLow} – ${valueHigh}`; if (this.#inputLow) this.#inputLow.value = valueLow; if (this.#inputHigh) this.#inputHigh.value = valueHigh; if (this.#inputLow && this.#inputHigh) { this.#trackColor(); } } }

#update() { const currentValue = `${this.#inputLow.value},${this.#inputHigh.value}`; this.#internals.setFormValue(currentValue); this.#trackColor(); this.#valueDisplay.innerHTML = `${this.#inputLow.value} – ${this.#inputHigh.value}`; }

#trackColor() { const valueLow = Number(this.#inputLow.value); const valueHigh = Number(this.#inputHigh.value); const percentLow = (valueLow - this.#Attributes.min) / (this.#Attributes.max - this.#Attributes.min) * 100; const percentHigh = (valueHigh - this.#Attributes.min) / (this.#Attributes.max - this.#Attributes.min) * 100; this.#doubleSlider.style.setProperty('--percentLow', percentLow + '%'); this.#doubleSlider.style.setProperty('--percentHigh', percentHigh + '%'); }

#round(zahl, epsilon) { if (epsilon>=1) { return Math.round(zahl/epsilon)*epsilon; } else { const n = Math.ceil(-Math.log10(epsilon)); return Number(zahl.toFixed(n)); } }

// Eventhandler // input: Für unterer Wert <= oberer Wert begrenzen sorgen, // Werte an Internals übergeben und anzeigen #handleInput(inputEvent) { if(inputEvent.target == this.#inputLow && Number(this.#inputHigh.value) < Number(this.#inputLow.value)) { this.#inputHigh.value = this.#inputLow.value; } else if(inputEvent.target == this.#inputHigh && Number(this.#inputHigh.value) < Number(this.#inputLow.value)) { this.#inputLow.value = this.#inputHigh.value; } this.#update(); }

// pointerdown: den näher am Pointer liegenden Schieber zum Pointer schieben, // Werte anzeigen #handlePointerdown(pointerdownEvent) { // Find the horizontal position that was clicked let clickValue = this.#Attributes.min + (this.#Attributes.max - this.#Attributes.min) * pointerdownEvent.offsetX / pointerdownEvent.target.offsetWidth; clickValue = this.#round(clickValue,this.#Attributes.step); const middleValue = (Number(this.#inputHigh.value) + Number(this.#inputLow.value))/2; if(clickValue < middleValue) { this.#inputLow.value = clickValue; } else { this.#inputHigh.value = clickValue; } this.#update(); this.dispatchEvent(new Event('input', { bubbles: true })); this.dispatchEvent(new Event('change', { bubbles: true })); }

// pointerover: Bei Bedarf Werteanzeige einblenden #handlePointerover(pointeroverEvent) { if(this.#Attributes.showvalues == 'active') this.#valueDisplay.className = 'ever'; }

// pointerout: Den Schiebern den Focus nehmen und bei Bedarf Werteanzeige ausblenden #handlePointerout(pointeroutEvent) { this.#inputLow.blur(); this.#inputHigh.blur(); this.#valueDisplay.className = this.#Attributes.showvalues; }

// change durchreichen #handleChange(changeEvent) { this.dispatchEvent(new Event('change', { bubbles: true })); }

#html() { return ` <label for='Low' slot='labellow'>Unterer Wert</label> <input id='Low' type='range' min=${this.#Attributes.min} max=${this.#Attributes.max} step=${this.#Attributes.step} value=${this.#Attributes.currentValue.split(',')[0]}></input> <label for='High' slot='labelhigh'>Oberer Wert</label> <input id='High' type='range' min=${this.#Attributes.min} max=${this.#Attributes.max} step=${this.#Attributes.step} value=${this.#Attributes.currentValue.split(',')[1]}></input> <output part='valuedisplay' role='status' aria-live='assertive' class='${this.#Attributes.showvalues}'></output> `; }

#css() { return `

host {

--trackInnerColor: lightblue; --trackOuterColor: lightgray; --thumbColor: blue; --thumbFocusColor: white; --thumbSize: 1.7em; --trackHeight: .3em; display: inline-block; width: 10em; height: 2em; }

  1. wrapper {

position: relative; display: grid; width: 100%; height: 100%; border: none; padding: 0; margin: 0; background-size: auto var(--trackHeight) !important; background-repeat: no-repeat !important; background-position-y: 50% !important; background: linear-gradient(to right, var(--trackOuterColor) var(--percentLow), var(--trackInnerColor) var(--percentLow), var(--trackInnerColor) var(--percentHigh), var(--trackOuterColor) var(--percentHigh) ); }

  1. wrapper label {

grid-area: 1 / 1 / 1 / 1; clip: rect(0 0 0 0); clip-path: inset(50%); width: 1px; height: 1px; overflow: hidden; white-space: nowrap; }

  1. wrapper output {

position:absolute; padding: 0; left: 50%; transform: translate(-50%, -.8em); background-color: white; pointer-events: none; display: none; }

  1. wrapper output.ever {

display: block; }

  1. wrapper:focus-within output.active {

display: block; }

  1. wrapper input {

grid-area: 1 / 1 / 1 / 1; -webkit-appearance: none; -moz-appearance: none; appearance: none; width: 100%; background-color: transparent; pointer-events: none; }

  1. wrapper input::-webkit-slider-runnable-track {

-webkit-appearance: none; appearance: none; }

  1. wrapper input::-moz-range-track {

-moz-appearance: none; appearance: none; }

  1. wrapper input::-webkit-slider-thumb {

-webkit-appearance: none; appearance: none; height: var(--thumbSize); width: var(--thumbSize); cursor: pointer; pointer-events: auto; border-radius: 50%; background-color: var(--thumbColor); }

  1. wrapper input::-moz-range-thumb {

-moz-appearance: none; appearance: none; height: var(--thumbSize); width: var(--thumbSize); cursor: pointer; pointer-events: auto; border-radius: 50%; background-color: var(--thumbColor); }

  1. wrapper input:is(:focus,:hover)::-webkit-slider-thumb {

background-color: var(--thumbFocusColor); border: medium solid var(--thumbColor); }

  1. wrapper input:is(:focus,:hover)::-moz-range-thumb {

background-color: var(--thumbFocusColor); border: medium solid var(--thumbColor); } `; }

}); // double-range-input


// Diverse Tests document.addEventListener('DOMContentLoaded', () => {

const out = document.querySelector('output'); const form = document.querySelector('form'); const button = form.querySelector('button');

// Element anlegen const label = document.createElement('label'); // label.innerHTML = 'Wert 4: Geben Sie Minimal- und Maximalwert an.
Die Vorgaben werden alle 5s geändert.'; label.innerHTML = 'Wert 4: Geben Sie Minimal- und Maximalwert an.'; const dr4 = document.createElement('double-range-input'); // Attributzuweisungen funktionieren auch mit setAttribute dr4.name = 'dr4'; dr4.min = -1; dr4.max = 1; dr4.step = 0.01; dr4.value = '-0.5, 0.5'; form.insertBefore(dr4,button); form.insertBefore(label,dr4);

const sliders = document.querySelectorAll('double-range-input');

// Startwerte abfragen sliders.forEach((v,i,a) => { console.log(i,v.name,v.value,v.form); if(i == 0) out.innerText = 'Startwerte:'; out.innerText += ` Element ${i}: ${v.name} ${v.value};`; });

// Werte bei input-Event abfragen form.addEventListener('input', event => { console.log('input', event.target.name, event.target.value); out.innerText = `input: Element ${event.target.name}: ${event.target.value};`; });

// Werte bei change-Event abfragen form.addEventListener('change', event => { console.log('change', event.target.name, event.target.value); out.innerText += ` change: Element ${event.target.name}: ${event.target.value};`; });

// Submit abfangen und Formularwerte anzeigen form.addEventListener('submit', (event) => { event.preventDefault(); const data = new FormData(event.target); console.log([...data.entries()]); out.innerText = ; [...data.entries()].forEach(v => out.innerHTML += `${v[0]}: ${v[1]}
`) });

// Value dynamisch ändern //setInterval(() => { let value = sliders[3].value; let valueLow = Number(value.split(',')[0]); let valueHigh = Number(value.split(',')[1]); let min = sliders[3].min; let max = sliders[3].max; valueLow += (Math.random()-.5)*(max-min)/10; valueHigh += (Math.random()-.5)*(max-min)/10; value = `${valueLow},${valueHigh}`; sliders[3].value = value; //}, 5000);

}); </script>

</head>

<body>

Min-Max-Schieberegler

<form method="get">

<label for="dr1">Wert 1: Geben Sie Minimal- und Maximalwert an.</label> <double-range-input name="dr1"></double-range-input> <label for="dr2">Wert 2: Geben Sie Minimal- und Maximalwert an.</label> <double-range-input name="dr2" min=10 max=60 showvalues="never"> <slot name='labellow'>Nicht weniger als</slot> <slot name='labelhigh'>Nicht mehr als</slot> </double-range-input> <label for="dr3">Wert 3: Geben Sie Minimal- und Maximalwert an.</label> <double-range-input name="dr3" value="20,30" min=-10 max=60 showvalues="active"></double-range-input>

<button>Submit</button>

</form>

<output></output>

</body> </html>