Beispiel:Boxmodell.html
<!DOCTYPE html> <html lang="de"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Boxmodell</title> <script>
"use strict"
// Custom-Element self-slider anlegen
customElements.define('self-slider', class extends HTMLElement {
// self-slider soll Formularelement sein
static formAssociated = true;
// Festlegen, welche Attribute überwacht werden sollen
static get observedAttributes() {
return ['legend', 'label', 'min', 'max', 'step', 'value'];
}
constructor() {
// Element wird anlgelegt
// super muss als erstes im constructor aufgerufen werden, super ruft den construcor der Elternklasse auf
super();
// Zugriff auf die ElementInternals, wird benötigt, um auf die Werte zuzugreifen, die mit dem Formular versendet werden.
this.internals = this.attachInternals();
// Schatten-DOM anlegen
// mode: 'open' : Vom Dokument aus ist der Zugriff auf das Schatten-Dom möglich.
// mode: 'closed' : Der Zugriff auf das Schatten-Dom ist nicht möglich.
const shadow = this.attachShadow({mode: 'closed'});
// Element für Inhalt, hier ein fieldset, anlegen und ins Schatten-DOM einhängen
this.slider = document.createElement('fieldset');
this.slider.setAttribute('part', 'fieldset');
shadow.appendChild(this.slider);
// CSS anlegen und ins Schatten-Dom einhängen
// :host selektiert das Custom Element
const style = document.createElement('style');
style.textContent = `
:host fieldset { max-inline-size: fit-content; }
:host fieldset * { display: inline-block; vertical-align: top; margin: 0; padding: .1em}
:host input { font-size: .9em; }
`;
shadow.appendChild(style);
// Defaultwerte der Attribute setzen
this.legendAttribute = ;
this.labelAttribute = ;
this.minAttribute = 0;
this.maxAttribute = 100;
this.stepAttribute = 1;
this.valueAttribute = 0;
}
connectedCallback() {
// Element wurde ins DOM eingehängt
// Schatten-DOM aufbauen
// Zufällige ID für das Number-Input, wird fürs Label benötigt
const numberID = Math.random().toString().replace('0.',);
// Legende
this.legend = document.createElement('legend');
this.legend.setAttribute('part', 'legend');
if(this.legendAttribute.length) {
this.legend.innerHTML = this.legendAttribute;
}
else {
this.legend.setAttribute('hidden', 'hidden');
}
this.slider.appendChild(this.legend);
// Label
this.label = document.createElement('label');
this.label.setAttribute('for', numberID);
this.label.setAttribute('part', 'label');
if(this.labelAttribute.length) {
this.label.innerHTML = this.labelAttribute;
}
else {
this.label.setAttribute('hidden', 'hidden');
}
this.slider.appendChild(this.label);
// Range-Input
this.range = document.createElement('input');
this.range.setAttribute('type', 'range');
this.range.setAttribute('min', this.minAttribute);
this.range.setAttribute('max', this.maxAttribute);
this.range.setAttribute('step', this.stepAttribute);
this.range.setAttribute('value', this.valueAttribute);
this.range.setAttribute('part', 'range');
this.slider.appendChild(this.range);
// Number-Input
this.number = document.createElement('input');
this.number.setAttribute('id', numberID);
this.number.setAttribute('type', 'number');
this.number.setAttribute('min', this.minAttribute);
this.number.setAttribute('max', this.maxAttribute);
this.number.setAttribute('step', this.stepAttribute);
this.number.setAttribute('value', this.valueAttribute);
this.number.setAttribute('part', 'number');
this.slider.appendChild(this.number);
// Start-Value speichern und ans Formular übergeben
this.currentValue = this.valueAttribute;
this.internals.setFormValue(this.currentValue);
// Eventhandler, um die beiden Inputs zu synchronisieren und den Wert zu speichern und ans Formular zu übergeben
this.slider.addEventListener('input', event => {
if(event.target.type == 'range') {
this.number.value = event.target.value;
}
else if(event.target.type == 'number') {
this.range.value = event.target.value;
}
this.currentValue= event.target.value;
this.internals.setFormValue(this.currentValue);
});
}
attributeChangedCallback(name, oldValue, newValue) {
// Elementparameter wurden geändert
// Achtung attributeChangedCallback wird vor connectedCallback aufgerufen, daher prüfen, ob Elemente schon vorhanden sind
switch(name) {
case 'legend':
this.legendAttribute = newValue;
if(this.legend) {
if(this.legendAttribute && this.legendAttribute.length) {
this.legend.removeAttribute('hidden');
this.legend.innerHTML = this.legendAttribute;
}
else {
this.legend.setAttribute('hidden','hidden');
}
}
break;
case 'label':
this.labelAttribute = newValue;
if(this.label) {
if(this.labelAttribute && this.labelAttribute.length) {
this.label.removeAttribute('hidden');
this.label.innerHTML = this.labelAttribute;
}
else {
this.label.setAttribute('hidden','hidden');
}
}
break;
case 'min':
this.minAttribute = newValue;
if(this.number) this.number.setAttribute('min', this.minAttribute);
if(this.range) this.range.setAttribute('min', this.minAttribute);
break;
case 'max':
this.maxAttribute = newValue;
if(this.number) this.number.setAttribute('max', this.maxAttribute);
if(this.range) this.range.setAttribute('max', this.maxAttribute);
break;
case 'step':
this.stepAttribute = newValue;
if(this.number) this.number.setAttribute('step', this.stepAttribute);
if(this.range) this.range.setAttribute('step', this.stepAttribute);
break;
case 'value':
this.currentValue = newValue;
this.valueAttribute = newValue;
if(this.number) this.number.setAttribute('value', this.valueAttribute);
if(this.range) this.range.setAttribute('value', this.valueAttribute);
this.internals.setFormValue(this.currentValue);
break;
}
}
// Setter und Getter für value.
set value(value) {
this.currentValue = value;
this.number.value = value;
this.range.value = value;
this.internals.setFormValue(value);
}
get value() {
return this.currentValue;
}
} );
</script>
<style> self-slider::part(label) { display: block; } self-slider::part(number) { width: 3em; text-align: right;} self-slider::part(range) { width: 10em; } self-slider::part(fieldset) { border: none; }
body {
max-width: 50em;
min-width: 50em;
--border-color: blue;
--margin-color: red;
--padding-color: green;
--border: 10px;
--margin: 20px;
--padding: 5px;
--border-block-start: 10px;
--border-block-end: 10px;
--border-inline-start: 10px;
--border-inline-end: 10px;
--margin-block-start: 20px;
--margin-block-end: 20px;
--margin-inline-start: 20px;
--margin-inline-end: 20px;
--padding-block-start: 5px;
--padding-block-end: 5px;
--padding-inline-start: 5px;
--padding-inline-end: 5px;
}
#border { color: var(--border-color); }
#margin { color: var(--margin-color); }
#padding { color: var(--padding-color); }
#border, #margin, #padding { display: inline-block; }
fieldset { width: fit-content; }
output {
display: inline-block;
vertical-align: top;
}
output > div {
background-color: var(--margin-color);
width: fit-content;
padding: 0;
display: inline-block;
}
output > div > div {
background-color: var(--padding-color);
width: 200px; height: 200px;
}
output.einfach > div > div {
border: var(--border) solid var(--border-color);
margin: var(--margin);
padding: var(--padding);
}
output.vollstaendig > div > div {
border-block-start: var(--border-block-start) solid var(--border-color);
border-block-end: var(--border-block-end) solid var(--border-color);
border-inline-start: var(--border-inline-start) solid var(--border-color);
border-inline-end: var(--border-inline-end) solid var(--border-color);
margin-block-start: var(--margin-block-start);
margin-block-end: var(--margin-block-end);
margin-inline-start: var(--margin-inline-start);
margin-inline-end: var(--margin-inline-end);
padding-block-start: var(--padding-block-start);
padding-block-end: var(--padding-block-end);
padding-inline-start: var(--padding-inline-start);
padding-inline-end: var(--padding-inline-end);
}
output > div > div > div {
background-color: white;
width: 100%;
height: 100%;
}
output:nth-of-type(odd) > div > div { box-sizing: content-box; }
output:nth-of-type(even) > div > div { box-sizing: border-box; }
</style>
<script> "use strict"
document.addEventListener("DOMContentLoaded", () => {
document.body.addEventListener("input", (inputEvent) => {
document.body.style.setProperty(`--${inputEvent.target.id}`,`${inputEvent.target.value}px`);
});
});
</script>
</head>
<body>
Inhaltsverzeichnis
Boxmodell
Einfach
<fieldset>
<self-slider id="margin" label="Margin" min="0" max="30" value="20" step="1"></self-slider>
<self-slider id="border" label="Border" min="0" max="30" value="10" step="1"></self-slider> <self-slider id="padding" label="Padding" min="0" max="30" value="5" step="1"></self-slider>
</fieldset>
<output class="einfach">
box-sizing: content-box
</output>
<output class="einfach">
box-sizing: border-box
</output>
Vollständig
<fieldset id="margin">
<legend>Margin</legend>
<self-slider id="margin-block-start" label="block-start" min="0" max="30" value="20" step="1"></self-slider>
<self-slider id="margin-block-end" label="block-end" min="0" max="30" value="20" step="1"></self-slider>
<self-slider id="margin-inline-start" label="inline-start" min="0" max="30" value="20" step="1"></self-slider>
<self-slider id="margin-inline-end" label="inline-end" min="0" max="30" value="20" step="1"></self-slider>
</fieldset>
<fieldset id="border">
<legend>Border</legend>
<self-slider id="border-block-start" label="block-start" min="0" max="30" value="10" step="1"></self-slider>
<self-slider id="border-block-end" label="block-end" min="0" max="30" value="10" step="1"></self-slider>
<self-slider id="border-inline-start" label="inline-start" min="0" max="30" value="10" step="1"></self-slider>
<self-slider id="border-inline-end" label="inline-end" min="0" max="30" value="10" step="1"></self-slider>
</fieldset>
<fieldset id="padding">
<legend>Padding</legend>
<self-slider id="padding-block-start" label="block-start" min="0" max="30" value="5" step="1"></self-slider>
<self-slider id="padding-block-end" label="block-end" min="0" max="30" value="5" step="1"></self-slider>
<self-slider id="padding-inline-start" label="inline-start" min="0" max="30" value="5" step="1"></self-slider>
<self-slider id="padding-inline-end" label="inline-end" min="0" max="30" value="5" step="1"></self-slider>
</fieldset>
<output class="vollstaendig">
box-sizing: content-box
</output> <output class="vollstaendig">
box-sizing: border-box
</output>
</body> </html>