Beispiel:Verkettete Auswahllisten-1.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">
	<link rel="stylesheet" media="screen" href="./Beispiel:Grundlayout.css">
<title>Verkettete Auswahllisten</title>
<script>
// Code steht in {...} wegen eines Frickl-Bugs
// Nun denkt der Linter, dies sei ein JSON-String und meckert auch. Bitte ignorieren!
{
	document.addEventListener('DOMContentLoaded', function () {
		const form = document.forms.terminauswahl; 
		const profCtrl = new ProfessorController(form.professor);
		const vorlesungCtrl = new VorlesungController(form.vorlesung, profCtrl);
		const terminCtrl = new TerminController(form.termin, vorlesungCtrl);
		terminCtrl.addEventListener("change", terminChanged)

		profCtrl.mapData(data)
	
		function terminChanged(event) {
			let terminCtrl = event.target;
			let vorlesungCtrl = terminCtrl.parentNode;
			let profCtrl = vorlesungCtrl.parentNode;

			let ausgabe = document.getElementById('auswahl');
			ausgabe.textContent = terminCtrl.selectedKey != ""
				?	`${profCtrl.selectedKey}:${profCtrl.selectedObject.name} / `+
					`${vorlesungCtrl.selectedKey}:${vorlesungCtrl.selectedObject.thema} / ` +
					`${terminCtrl.selectedKey}:${terminCtrl.selectedObject.zeit}`
				:	"";
		}
	});

	// SelectionController ist eine abstrakte Basisklasse für die konkreten Controller, die 
	// zur Anbindung der select Elemente dienen
	// Abstrakte Methoden sind
	// getValue(key) - muss das Datenobjekt liefern, das zu einer ausgewählten option gehört.
	// Als key wird der Wert des value-Attributs dieser Option übergeben.
	// getValueList(dataSource) - muss Daten für die option-Elemente liefern, die im select 
	// Element einzutragen sind. Ergebnis muss ein Array aus Objekten sein, jedes Objekt muss
	// die Properties value (für das value-Attribut) und text (für den Inhalt des option Elements)
	// enthalten
	class SelectionController extends EventTarget {
		constructor(selectElement, parentNode = null) {
			super();		// Pflicht: Konstruktor der Superklasse aufrufen
			if (!(selectElement instanceof HTMLSelectElement)) {
				throw new Error("Controller-Objekt benötigt ein select Element als ersten Parameter");
			}
			if (parentNode && !(parentNode instanceof SelectionController)) {
				throw new Error("Controller-Objekt benötigt einen SelectionController als zweiten Parameter");
			}

			this.selectElement = selectElement;
			this.parentNode = parentNode;

			this.selectElement.addEventListener("change", event => this._handleChangeEvent(event))
			if (parentNode) {
				parentNode.addEventListener("change", event => this.mapData(event.selectedObject));
			}
		}

		// Ordnet dem select Element eine Datenquelle zu. 
	        // dataSource ist ein Objekt, aus dem die getValueList die Daten für die
        	// select-Optionen ermitteln kann, oder null, um das select-Element zu
	        // disablen
		mapData(dataSource) {
			// Quelldaten-Objekt im Controller speichern.
			this.dataSource = dataSource;
    
			// Optionen nur anfassen, wenn eine getValueList Methode vorhanden ist.
			//  Andernfalls davon ausgehen, dass die options durch das HTML
			// bereitgestellt werden.
			if (typeof this.getValueList == "function") {

				// Existierende Optionen entfernen
				removeOptions(this.selectElement);

				// Wenn dataSource nicht null war, die neuen Optionen daraus beschaffen
				// Andernfalls das select-Element deaktivieren
				const options = dataSource && this.getValueList(dataSource);
				if (!options || !options.length) {
					setToDisabled(this.selectElement)
				} else {
					setToEnabled(this.selectElement, options);
				}
			}

			// Zum Abschluss ein change-Event auf dem select-Element feuern, damit
			// jeder weiß, dass hier etwas passiert ist
			this.selectElement.dispatchEvent(new Event("change"));

			// Helper: Entferne alle options aus einem select Element	
			function removeOptions(selectElement) {
				while (selectElement.length > 0)
					selectElement.remove(0);
			}

			// Helper: select-Element auf disabled setzen und eine Dummy-Option 
			// eintragen. Eine Variante wäre: das selectElement auf hidden setzten
			function setToDisabled(selectElement) {
				addOption(selectElement, "", "------");
				selectElement.disabled = true;
			}

			// Helper: disabled-Zustand vom select-Element entfernen und die
			// übergebenen Optionen eintragen. Vorweg eine Dummy-Option "Bitte wählen".
			function setToEnabled(selectElement, options) {
				addOption(selectElement, "", "Bitte wählen:");

				for (var optionData of options) {
					addOption(selectElement, optionData.value, optionData.text);
				}
				selectElement.disabled = false;
			}
    
			// Helper: Option-Element erzeugen, ausfüllen und im select-Element eintragen
			function addOption(selectElement, value, text) {
				let option = document.createElement("option");
				option.value = value;
				option.text = text
				selectElement.add(option);
			}
		}

		// Abstrakte Methode! Wird sie nicht überschrieben, wird der TypeError geworfen
		getValue(key) {
			throw new TypeError("Die abstrakte Methode 'getValue' wurde nicht implementiert!");
		}

		// Stellt den im select Element ausgewählten Optionswert zur Verfügung
		get selectedKey() {
			return this.selectElement.value;
		}
	
		// Liefert das Datenobjekt zum ausgewählten Optionswert
		get selectedObject() {
			return this.dataSource ? this.getValue(this.dataSource, this.selectElement.value) : null;
		}

		// privat
		// Die Methode reagiert auf das change-Event des select-Elements
		// und stellt es als eigenes change-Event des Controllers zur Verfügung
		_handleChangeEvent(event) {
			let nodeChangeEvent = new Event("change");
			nodeChangeEvent.selectedObject = this.selectedObject;
			this.dispatchEvent(nodeChangeEvent);
		}
	}

	class ProfessorController extends SelectionController {
		constructor(selectElement) {
			super(selectElement, null);
		}

		getValue(quelle, profId) {
			return quelle.find(prof => prof.id == profId);  
		}
	}

	class VorlesungController extends SelectionController {
		constructor(selectElement, parentController) {
		super(selectElement, parentController);
		}
		getValueList(professor) {
			return professor.vorlesungen.map(vorlesung => ({ value: vorlesung.nummer, text: vorlesung.thema }));
		}
		getValue(professor, vorlesungNr) {
			return professor.vorlesungen.find(vorlesung => vorlesung.nummer == vorlesungNr); 
		}
	}

	class TerminController extends SelectionController {
		constructor(selectElement, parentController) {
			super(selectElement, parentController);
		}
		getValueList(vorlesung) {
			return vorlesung.termine.map(termin => ({ value: termin.id, text: termin.zeit }));
		}
		getValue(vorlesung, terminId) {
			return vorlesung.termine.find(termin => termin.id == terminId); 
		}
	}
}
var data = [
	{	id: "P23",
		name: "Albers, Alfred",
		vorlesungen: [
			{	nummer: "L55",
				thema: "Katzen in der freien Wildbahn",
				termine: [
					{ id: "T123", zeit: "freitags 10 - 12" },
					{ id: "T124", zeit: "sonntags 18 - 20" }
				]
			},
			{	nummer: "L56",
				thema: "Katzen im Gefängnis",
				termine: [
					{ id: "T125", zeit: "montags 12 - 14" },
					{ id: "T126", zeit: "dienstags 8 - 10" }
				]
			},
			{	nummer: "L57",
				thema: "Katzen in Musicals",
				termine: [
					{ id: "T127", zeit: "dienstags 10 - 12" },
					{ id: "T128", zeit: "mittwochs 13 - 15" }
				]
			}
		]
	},
	{	id: "P24",
		name: "Braun, Berta",
		vorlesungen: [
			{	nummer: "L65",
				thema: "Hunde im Himmel",
				termine: [
					{ id: "T163", zeit: "freitags 10 - 12" },
					{ id: "T164", zeit: "sonntags 18 - 20" }
				]
			},
			{	nummer: "L66",
				thema: "Hunde in der Hölle",
				termine: [
					{ id: "T165", zeit: "montags 12 - 14" },
					{ id: "T166", zeit: "dienstags 8 - 10" }
				]
			},
			{	nummer: "L67",
				thema: "Höllenhunde",
				termine: [
					{ id: "T167", zeit: "dienstags 10 - 12" },
					{ id: "T168", zeit: "mittwochs 13 - 15" }
				]
		}
	  ]
	},
	{	id: "P25",
		name: "Drachenzaun, Doris",
		vorlesungen: [
			{	nummer: "L75",
				thema: "Wirtschaftskram I",
				termine: [
					{ id: "T171", zeit: "freitags 10 - 12" },
					{ id: "T172", zeit: "sonntags 18 - 20" }
				]
			},
			{	nummer: "L76",
				thema: "Wirtschaftskram II",
				termine: [
					{ id: "T173", zeit: "montags 12 - 14" },
					{ id: "T174", zeit: "dienstags 8 - 10" }
				]
			},
			{	nummer: "L77",
				thema: "Wirtschaftskram III",
				termine: [
					{ id: "T175", zeit: "dienstags 10 - 12" },
					{ id: "T176", zeit: "mittwochs 13 - 15" }
				]
			}
		]
	},
	{	id: "P26",
		name: "Münz, Stefan",
		vorlesungen: [
			{	nummer: "L85",
				thema: "Webdesign in den 90ern",
				termine: [
					{ id: "T181", zeit: "freitags 10 - 12" },
					{ id: "T182", zeit: "sonntags 18 - 20" }
				]
			},
			{	nummer: "L86",
				thema: "Webdesign nach der Jahrtausendwende",
				termine: [
					{ id: "T183", zeit: "montags 12 - 14" },
					{ id: "T184", zeit: "dienstags 8 - 10" }
				]
			},
			{	nummer: "L87",
				thema: "Responsives Webdesign",
				termine: [
					{ id: "T185", zeit: "dienstags 10 - 12" },
					{ id: "T186", zeit: "mittwochs 13 - 15" }
				]
			}
		],
	},
	{	id: "P27",
		name: "Meier, Manfred",
		vorlesungen: [
			{	nummer: "L95",
				thema: "Althochdeutsch",
				termine: [
					{ id: "T191", zeit: "freitags 10 - 12" },
					{ id: "T192", zeit: "sonntags 18 - 20" }
				]
			},
			{	nummer: "L96",
				thema: "Mittelhochdeutsch",
				termine: [
					{ id: "T193", zeit: "montags 12 - 14" },
					{ id: "T194", zeit: "dienstags 8 - 10" }
				]
			},
			{	nummer: "L97",
				thema: "Deutsch für Angefangene",
				termine: [
					{ id: "T195", zeit: "dienstags 10 - 12" },
					{ id: "T196", zeit: "mittwochs 13 - 15" }
				]
			}
		]
	}
];

</script>
<style>
label {
	display:block;
	margin: 1em;
	font-size: 1.5em;
}

select {
	display: block;
	font-size: inherit;
	padding: .6em 1.4em .5em .8em;
	width: 20em;
	max-width: 100%; /* useful when width is set to anything other than 100% */
	box-sizing: border-box;
	margin: 0;
	border: 1px solid #aaa;
	box-shadow: 0 1px 0 1px rgba(0,0,0,.04);
	border-radius: .5em;
	-webkit-appearance: none;
	appearance: none;
	background-color:  transparent;
    /* note: bg image below uses 2 urls. The first is an svg data uri for the arrow icon, and the second is the gradient. 
        for the icon, if you want to change the color, be sure to use `%23` instead of `#`, since it's a url. You can also swap in a different svg icon or an external image reference
    */
    background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23007CB2%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E'),
		linear-gradient(to bottom, #ffffff 0%,#e5e5e5 100%);
	background-repeat: no-repeat, repeat;
	/* arrow icon position (1em from the right, 50% vertical) , then gradient position*/
	background-position: right .7em top 50%, 0 0;
	/* icon size, then gradient */
	background-size: .65em auto, 100%;
}
/* Hover style */
select:hover {
	border-color: #888;
}

/* Focus style */
select:focus {
	border-color: #aaa;
	/* It'd be nice to use -webkit-focus-ring-color here but it doesn't work on box-shadow */
	box-shadow: 0 0 1px 3px rgba(59, 153, 252, .7);
	box-shadow: 0 0 0 3px -moz-mac-focusring;
	color: #222; 
	outline: none;
}

/* Set options to normal weight */
select option {
	font-weight:normal;
}

select > *:hover {
	background-color: #dfac20;
}
</style>
</head>

<body>
<h1>Verkettete Auswahllisten</h1>

<form name="terminauswahl">
  <label>Professor:
    <select id="professor">
      <option value="">Bitte wählen:</option>
      <option value="P23">Albers, Alfred</option>
      <option value="P24">Braun, Berta</option>
      <option value="P25">Drachenzaun, Doris</option>
      <option value="P26">Münz, Stephan</option>
      <option value="P27">Meier, Manfred</option>
    </select>
  </label>
  <label>Lesung:
    <select id="vorlesung" disabled>
      <option value="">------</option>
    </select>
  </label>
  <label>Termin:
    <select id="termin" disabled>
      <option value="">------</option>
    </select>
  </label>
</form>
<p>
   <output id="auswahl"></output>
</p>


</body>
</html>