wien

Wien Beispiel HOWTO (Teil 6) - Hotelsuche mit HTML Formular und Autocomplete

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

1. Das Suchformular

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>

🔗 COMMIT

2. Eine Autocomplete-Liste erstellen

Ü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>

🔗 COMMIT

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">

🔗 COMMIT

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.

3. Die Suchliste mit den Hotelnamen füllen

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>`;

🔗 COMMIT

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.

4. Hotel suchen, anzeigen und Popup ausklappen

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);
}

🔗 COMMIT

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)
    })
}

🔗 COMMIT

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();
        }
    });
}

🔗 COMMIT

Unsere Testeintäge eins, zwei, drei im <datalist> Element können wir auch noch löschen

🔗 COMMIT

5. Leider ist die Suchliste nicht sortiert :-(

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()
})

🔗 COMMIT