Beispiel:BMI-Rechner.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>BMI-Rechner</title>
		<script> <!-- Script für den self-slider -->
      "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: 4em; text-align: right;}
			self-slider::part(range)    { width: 20em; }
			self-slider::part(fieldset) { border: none; }

      meter { 
        --meterWidth: 20rem;
        --extremeColor: red;
        width: var(--meterWidth);
      }

      .safari {
        meter.extreme { 
          -webkit-appearance: none;
          appearance: none;
          box-sizing: border-box
        }
        meter.extreme::-webkit-meter-suboptimum-value {
          background: var(--extremeColor);
          border-radius: 50cqmin;
        }
        meter.extreme::-webkit-meter-bar {
          background: rgb(232 232 232);
          border-radius: 50cqmin;
          height: 14px;
          margin-top: 1px;
          margin-left:1px;
          width: calc(var(--meterWidth) - 2px)
        }
      }

      .firefox {
        meter.extreme { 
          appearance: none;
          border: 1px solid #888;
          border-radius: 50cqmin;
          background: rgb(232 232 232);
          height: 12px;
          position: relative;
          top: -2px;
          box-sizing: border-box;
        }
        meter.extreme::-moz-meter-bar {
          background: var(--extremeColor);
          border-top-left-radius: 50cqmin;
          border-bottom-left-radius: 50cqmin;
          height: 12px;
        }
      }

      .chrome, .egde {
        meter.extreme::-webkit-meter-suboptimum-value {
          background: var(--extremeColor);
          border-top-right-radius: 0;
          border-bottom-right-radius: 0;
        }
      }

      p { max-width: 30em;}
      
		</style>
		
		<script> <!-- Script zum berechnen des BMI -->
			"use strict"

// Browser-Check
// Source - https://stackoverflow.com/a/52445821
// Posted by Christopher Martin
// Retrieved 2026-07-17, License - CC BY-SA 4.0
// Modified by selfhtml

      function checkBrowser() {
        if(navigator.vendor.match(/google/i)) {
          document.body.classList.add("chrome");
        }
        else if(navigator.vendor.match(/apple/i)) {
          document.body.classList.add("safari");
        }
        else if(navigator.userAgent.match(/firefox\//i)) {
          document.body.classList.add("firefox");
        }
          else if(navigator.userAgent.match(/edge\//i)) {
            document.body.classList.add("edge");
        }
      }

      document.addEventListener('DOMContentLoaded', () => {   
        checkBrowser();

				const weight_in = document.querySelector("#weight_in");
				const height_in = document.querySelector("#height_in");
				const weight_out = document.querySelector("#weight_out");
				const height_out = document.querySelector("#height_out");
				const bmi_out = document.querySelector("#bmi_out");
				const bmi_meter = document.querySelector("#bmi_meter");
				const category_out = document.querySelector("#category_out");

				document.body.addEventListener("input", (input_event) => {
					calc_bmi();
				});

        calc_bmi();
			});

			function calc_bmi() {
				let weight = Number(weight_in.value);
				let height = Number(height_in.value);
				let bmi = weight / Math.pow(height,2);

        let category;
        if (bmi <17 ) {
         category = "Du hast Magersucht.";
          bmi_meter.classList.add("extreme");
        }
        else if (bmi < 20) {
          category = "Du hast Untergewicht.";
          bmi_meter.classList.remove("extreme");
        } else if (bmi < 25) {
          category = "Du hast Normalgewicht.";
          bmi_meter.classList.remove("extreme");
        } else if (bmi < 30) {
          category = "Du hast Übergewicht.";
          bmi_meter.classList.remove("extreme");
        } else {
          category = "Du bist Fettleibig.";
          bmi_meter.classList.add("extreme");
        }

				weight_out.innerText = weight.toLocaleString( undefined, {minimumFractionDigits: 1, maximumFractionDigits: 1});
				height_out.innerText = height.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
				bmi_out.innerText    = bmi.toLocaleString(undefined, {minimumFractionDigits: 1, maximumFractionDigits: 1});
        bmi_meter.value = bmi;
        bmi_meter.innerText = bmi.toLocaleString(undefined, {minimumFractionDigits: 1, maximumFractionDigits: 1});
        category_out.innerText = category;
			}
		</script>

	</head>

	<body>
		<h1>BMI-Rechner</h1>

		<self-slider id="weight_in" label="Gewicht in kg" min="40" max="200" value="70" step="0.5"></self-slider>

		<self-slider id="height_in" label="Größe in m" min="1.2" max="2.2" value="1.8" step="0.01"></self-slider>

		<p>Bei einem Gewicht von <output id="weight_out">--</output> kg und einer Größe von <output id="height_out">--</output> m beträgt der BMI <output id="bmi_out">--</output>.</p>
    <meter id="bmi_meter" min="10" low="20" high="25" max="40"">BMI</meter>
    <p><output id="category_out">--</output></p>

    <p>Diese Seite ist ein Beispiel für das Erfassen, Verarbeiten und Anzeigen von Daten. Der Autor besitzt kein medizinisches Fachwissen und die Interpretation der Werte erfolgt nach diversen Quellen aus dem Internet, deren Glaubwürdigkeit nicht geprüft wurde.</p>

	</body>
</html>