Bei 360 Hotels und Unterkünften ist eine Suche nach Hotelnamen sehr hilfreich. Wir implementieren sie mit den HTML Elementen <form>, <input> und <datalist>, einem Event Handler beim Abschicken des Suchformulars und der Leaflet Methode .eachLayer
zum Finden des gesuchten Hotels in der Hotelfunktion loadHotels
Wir beginnen mit dem HTML Code für das Formular. Es besteht aus einem Suchfeld, einem Suchbutton und einem Resetbutton. Wir schreiben den Markup in index.html
direkt vor dem <div> Element der Karte:
<form id="searchForm">
<input type="text" name="hotel" placeholder="Hotel suchen ...">
<input type="button" name="suchen" value="Anzeigen">
<input type="reset" value="Reset">
</form>
nachdem wir später im Skript auf den Inhalt des Formulars zugreifen wollen, vergeben wir beim <form> Element eine ID (id="searchForm"
) - über sie werden wir Zugang zum Formular bekommen
drei <input> Element mit drei verschiedenen Typen komplettieren das Formular
input type="text"
definiert das Suchfeld. Über name="hotel"
geben wir ihm den Namen hotel
, den wir später zum Auslesen des gesuchten Hotels verwenden können. Ein Platzhalter (placeholder
) als Eingabehilfe wird angezeigt, solange das Suchfeld leer ist
input type="button"
definiert den Button zum Abschicken des Formulars. Wir vergeben auch hier eine Namen (suchen
), den wir später verwenden werden, um auf Klicks auf den Button zu reagieren. Über value
setzen wir den Label des Buttons auf Anzeigen
input type="reset"
definiert den Button zum Zurücksetzen des Formulars. Er kann bestehenden Text im Textfeld löschen und den Platzhalter damit wieder anzeigen. Über value
setzen wir den Label des Buttons auf Reset
Über das <datalist> Element können wir die Autocomplete-Funktionalität vorbereiten. Dazu wird innerhalb des <datalist> Elements für jeden Wert ein <option> Element mit dem entsprechenden Wert als value
Attribut definiert. Wir schreiben drei Einträge für die Texte eins, zwei, drei. Das <datalist> Element bekommt zusätzlich eine id="searchList"
die wir gleich noch brauchen werden.
<datalist id="searchList">
<option value="eins"></option>
<option value="zwei"></option>
<option value="drei"></option>
</datalist>
Wie wir beim Neuladen des Browsers erkennen, werden <datalist> Elemente nicht direkt angezeigt. Damit wir sie beim Suchfeld als Vorschläge auswählen können, müssen wir über list="searchList"
beim Suchfeld vermerken, dass die Datenliste mit der ID searchList für das Autocomplete herangezogen werden soll.
<input type="text" name="hotel" placeholder="Hotel suchen ..." list="searchList">
Das fertige Formular mit Autoccomplete über die Suchliste sieht damit so aus:
<form id="searchForm">
<input type="text" name="hotel" placeholder="Hotel suchen ...">
<input type="text" name="hotel" placeholder="Hotel suchen ..." list="searchList">
<input type="button" name="suchen" value="Anzeigen">
<input type="reset" value="Reset">
</form>
<datalist id="searchList">
<option value="eins"></option>
<option value="zwei"></option>
<option value="drei"></option>
</datalist>
Mit wenigen Buchstaben können wir jetzt auf die passenden Einträge der Suchliste zugreifen.
Unsere Suchvorschläge eins, zwei,drei helfen uns natürlich nicht bei der Hotelsuche, deshalb füllen wir das <datalist> Element mit den tatsächlichen Hotelnamen. Der Platz an dem das passieren kann, ist in main.js
in der pointToLayer
Funktion. Dort wird ja für jedes Hotel der Inhalt der Funktion pointToLayer
ausgeführt. Wir ergänzen deshalb ganz oben in der pointToLayer
Funktion den folgenden Code:
let searchList = document.querySelector("#searchList");
searchList.innerHTML += `<option value="${geoJsonPoint.properties.BETRIEB}"></option>`;
document.querySelector()
kennen wir schon vom Neuseelandbeispiel. Es findet über den CSS-Selektor #searchList
das Element mit der ID searchList
- in unserem Fall also das <datalist> Element und speichert es in der Varaiablen searchList
. Über searchList.innerHTML
können wir dann den Quellcode des <datalist> Elements verändern. Wir verwenden +=
um Markup an den bestehenden Inhalt anzuhängen.
für jedes Hotel wird dann ein <option> Element mit dessen Namen in geoJsonPoint.properties.BETRIEB
als value
angehängt
Das Resultat können wir zwar wieder nicht direkt sehen, aber im Suchfeld testen - alle Hotels sind jetzt über Autocomplete mit wenigen Buchstaben wählbar.
Der Platz an dem das alles passiert ist am Ende von loadHotels
. Damit wir dort mit dem Formular kommunizieren können, müssen wir uns zuerst mit document.querySelector
eine Referenz zum Formular mit der ID searchForm
in einer Variablen form
speichern.
let form = document.querySelector("#searchForm");
Danach können wir bei der Variablen form
einen sogenannten Event-Listener definieren, dessen Funktion ausgeführt wird, wenn wir auf den Anzeigen-Button form.suchen
klicken (onclick
). In dieser Funktion lassen wir uns den aktuellen Wert des Suchfelds in form.hotel.value
mit console.log
anzeigen.
form.suchen.onclick = function() {
console.log(form.hotel.value);
}
In der Variablen form.hotel.value
wissen wir jetzt also, welches Hotel in der Suche ausgewählt wurde. Wie finden wir den dazugehörigen Marker mit Popup in unserer Karte? Dazu müssen wir alle gezeichneten Marker im GeoJSON Layer durchgehen und deren Namen mit dem gewünschten Namen vergleichen.
Für dieses Durchgehen, stellt Leaflet die Methode .eachLayer zur Verfügung. Wir können sie anwenden, sobald der L.geoJSON()
Aufruf in einer Variablen gespeichert ist. Wir ergänzen also vor dem L.geoJSON()
Aufruf die Deklaration für die Variablen hotelsLayer
let hotelsLayer = L.geoJSON(geojson, {
// bestehender Code zum Zeichnen der Marker
})
In der form.suchen.onclick
Funktion können wir dann hotelsLayer.eachLayer()
einsetzen, um jeden Marker zu untersuchen. Wir lassen uns den marker, die Koordinate über marker.getLatLng(), das Popup über marker.getPopup() und den Namen über marker.feature.properties.BETRIEB
mit console.log
anzeigen.
form.suchen.onclick = function() {
console.log(form.hotel.value);
hotelsLayer.eachLayer(function(marker) {
console.log(marker)
console.log(marker.getLatLng())
console.log(marker.getPopup())
console.log(marker.feature.properties.BETRIEB)
})
}
In einer if-Abfrage
überprüfen wir schließlich noch, ob der aktuelle Name des Hotels in marker.feature.properties.BETRIEB
dem gesuchten Namen in form.hotels.value
entspricht. Sind beide gleich, positionieren wir die Karte mit map.setView() auf die Koordinate des Icons. Als Zoomlevel wählen wir dabei 17, denn in diesem Level sind alle Markercluster aufgelöst. Mit marker.openPopup() öffnen wir noch das Popup. Die console.log
Zeilen kommentieren wir aus, oder löschen sie ganz.
// Hotel suchen und anzeigen
let form = document.querySelector("#searchForm");
form.suchen.onclick = function() {
hotelsLayer.eachLayer(function (marker) {
if (form.hotel.value == marker.feature.properties.BETRIEB) {
map.setView(marker.getLatLng(), 17);
marker.openPopup();
}
});
}
Unsere Testeintäge eins, zwei, drei im <datalist> Element können wir auch noch löschen
Leider sind die Einträge der Suchliste nicht nach dem Attribut BETRIEB
sortiert. Deshalb sortieren wir die Einträge im geojson.features
Array einfach selber. Wie geht das? Gar nicht so einfach, aber hier ist die Lösung ;-)
geojson.features.sort(function(a, b) {
return a.properties.BETRIEB > b.properties.BETRIEB;
});
Details dazu hat die MDN Hilfe
Noch besser ist es, die Namen gleich case insensitive zu vergleichen. Die Javascript String Methode .toLowerCase() erlaubt das und unsere fertige Sortierfunktion sieht damit so aus:
geojson.features.sort(function(a, b) {
return a.properties.BETRIEB.toLowerCase() > b.properties.BETRIEB.toLowerCase()
})