Live-Demo

Probier das Widget direkt aus. Wähle oben zwischen der schlanken Spielwiese und der ausführlichen Variante mit sichtbarer API-Kommunikation.

Tipp einen Ortsnamen ein, klick einen Vorschlag an. Unten siehst du die Werte, die das Formular beim Absenden an einen Server schicken würde.

Pfeiltasten ↑/↓ zum Navigieren · Enter oder Klick zum Auswählen · Esc zum Schließen.

Was beim Form-Submit als $_POST ankäme

Noch nichts ausgewählt — Werte erscheinen hier nach der Auswahl.

Dieselbe Eingabe wie nebenan — aber zusätzlich machen wir transparent, was im Hintergrund passiert: welche URL wird abgefragt, was kommt vom Server zurück, und wie verwandelt sich das in $_POST-Werte für deinen PHP-Code.

Tippe etwas und beobachte die drei Panels unten.

1. Anfrage

URL erscheint hier, sobald du tippst.

2. Antwort (rohe JSON)

JSON-Antwort erscheint hier.

3. $_POST nach Auswahl

Werte erscheinen nach Auswahl.
Panel 1 zeigt jeden HTTP-Aufruf, den das Widget macht. Panel 2 die Server-Antwort. Panel 3 die Hidden-Felder, die das Widget nach deiner Auswahl füllt — diese würden bei einem Form-Submit als $_POST-Variablen am Server ankommen.
Was du gerade beobachtest: Bei jedem Tastendruck (mit 200 ms Pause als Bremse) ruft das Widget https://geoapi.world/api/search.php?q=… auf. Die Antwort ist eine JSON-Liste mit Treffern. Sobald du einen wählst, schreibt das Widget mehrere unsichtbare Hidden-Felder in dein Formular — und genau diese Werte liegen in $_POST für dein Server-PHP an.

Der komplette Quelltext der schlanken Demo

Genau dieser Code erzeugt den Widget-Tab oben. Du kannst ihn 1:1 als Vorlage nehmen.

<!DOCTYPE html>
<html lang="de">
<head><meta charset="utf-8"><title>Mein Eingabeformular</title></head>
<body>

  <form method="post" action="speichern.php">
    <label>Geburtsort:
      <input type="text" name="ort" data-geoapi-place
             placeholder="Ort eingeben">
    </label>
    <button type="submit">Absenden</button>
  </form>

  <script src="https://geoapi.world/dist/geoapi-widget.js"></script>

</body>
</html>

So zerlegt sich der Code

1Das Eingabefeld

<input type="text" name="ort" data-geoapi-place>

Ein normales HTML-Input. Das Attribut data-geoapi-place ist die Markierung, an der das Widget erkennt: „hier soll Autosuggest hin". name="ort" bestimmt, wie das Feld später im PHP heißt: $_POST["ort"].

2Die Skript-Zeile

<script src="https://geoapi.world/dist/geoapi-widget.js"></script>

Diese eine Zeile lädt das Widget aus unserem Server. Der Browser kümmert sich automatisch darum — du musst nichts installieren oder herunterladen.

3Die unsichtbaren Hidden-Felder

Sobald du einen Ort wählst, baut das Widget zusätzliche Felder ins Formular, die du nicht siehst, die aber mit abgesendet werden:

  • $_POST["ort_geonameid"] — eindeutige Kennung (z. B. 2867714 für München)
  • $_POST["ort_timezone"] — IANA-Zeitzone (Europe/Berlin)

Optional zusätzlich (siehe data-geoapi-lat, -lon, -country — wie im Detail-Tab oben aktiv): Koordinaten und Ländercode.

4Was dein Server-Skript damit macht

<?php
$ort       = trim($_POST["ort"]             ?? "");
$geonameid = (int) ($_POST["ort_geonameid"] ?? 0);
$timezone  = trim($_POST["ort_timezone"]    ?? "");

if ($geonameid === 0) {
    die("Bitte einen Vorschlag aus der Liste wählen.");
}

echo "Du hast gewählt: $ort";
echo " (geoNames-ID: $geonameid)";
echo " in der Zeitzone $timezone";

Wie geht es weiter?

Vier Bausteine, mit denen du das Widget an deine Anwendung anpassen kannst — alle live ausprobierbar. Jede Box zeigt das Eingabefeld plus genau den HTML-Code, der diese Variante erzeugt.

AEigenes Format für Liste & Eingabefeld

Default-Anzeige: „München, Bayern (DE)" mit Zeitzone und Einwohnerzahl als Untertitel. Mit data-geoapi-format-list und data-geoapi-format-input baust du das Layout selbst. Platzhalter {key} für Standardfelder (name, country_code, admin1_code, timezone, population, lat, lon, geonameid, feature_code) und Dot-Pfade {a.b} für extended-Blöcke ({admin1.name}, {country.name}).

<input type="text" name="fmt" data-geoapi-place
       data-geoapi-lang="de"
       data-geoapi-format-list="<strong>{name}</strong>, {admin1.name} ({country_code})<small>{population} Einw. · {timezone}</small>"
       data-geoapi-format-input="{name}">
Hinweis zu {admin1.name}: kommt aus dem extended-Block admin, der seit Default auch ohne explizites data-geoapi-extended mitgeladen wird. Mit data-geoapi-lang="de" wird der Name lokalisiert ausgeliefert („Bayern" statt „Bavaria"). Mehr Felder unter Format-Templates: verfügbare Keys.

BDaten automatisch in andere Felder/Anzeigen schreiben

Setze data-geoapi-fill="key" auf jedem beliebigen HTML-Element — sobald der Nutzer auswählt, wird das Element befüllt. Bei <input>/<textarea> in den value, sonst als Textinhalt.

Land: Zeitzone: Einwohner: Koordinaten: , Notizfeld:
<input type="text" name="meinort" data-geoapi-place>

<p>Land:        <span data-geoapi-fill="country_code"></span></p>
<p>Zeitzone:    <span data-geoapi-fill="timezone"></span></p>
<p>Einwohner:   <span data-geoapi-fill="population"></span></p>
<p>Koordinaten: <span data-geoapi-fill="lat"></span>,
                <span data-geoapi-fill="lon"></span></p>

<input type="text" data-geoapi-fill="name">
Mehrere Eingabefelder auf einer Seite? Standardmäßig gehört jedes Fill-Element zum nächsten Place-Input, der im DOM davor steht — solange deine Anzeige-Felder unter ihrem Eingabefeld stehen, klappt das automatisch. Falls nicht, explizit binden mit data-geoapi-fill-from="inputname".

CDebug-Modus: alle Werte sichtbar

Im Entwicklungsbetrieb hilfreich: data-geoapi-debug zeigt unterhalb des Eingabefelds eine Box, in der alle gerade gesetzten Werte sichtbar sind — die Hidden-Felder, die sonst niemand sieht. Im Produktivbetrieb das Attribut weglassen.

<input type="text" name="dbgort" data-geoapi-place
       data-geoapi-lat="dbgort_lat"
       data-geoapi-lon="dbgort_lon"
       data-geoapi-country="dbgort_cc"
       data-geoapi-debug>

DLayout der Auswahlliste über CSS

Alles am Aussehen ist über CSS-Variablen anpassbar — und zur Not können auch die internen Klassen direkt überschrieben werden. Probier folgende Variante mit warmen Farben:

<style>
.stage-warm {
  --geoapi-bg:           #fef3c7;
  --geoapi-fg:           #1f2937;
  --geoapi-border:       #f59e0b;
  --geoapi-border-light: #fde68a;
  --geoapi-active-bg:    #fbbf24;
  --geoapi-radius:       8px;
  --geoapi-shadow:       0 6px 16px rgba(245,158,11,0.25);
  --geoapi-padding-x:    0.9rem;
  --geoapi-padding-y:    0.55rem;
}
</style>

<div class="stage-warm">
  <input type="text" name="stilort" data-geoapi-place>
</div>

Vollständige Variablen-Liste:

--geoapi-bg, --geoapi-fg, --geoapi-muted,
--geoapi-border, --geoapi-border-light,
--geoapi-active-bg, --geoapi-radius,
--geoapi-shadow, --geoapi-max-height,
--geoapi-padding-x, --geoapi-padding-y,
--geoapi-font-size, --geoapi-z-index

Reicht das nicht, übersteure direkt die Klassen .geoapi-suggest-list, .geoapi-suggest-list li, .geoapi-suggest-list li.geoapi-active und .geoapi-suggest-list small.

EZeitverschiebung pro Datum (DST-aware)

Die statische Zeitzone (Europe/Berlin) reicht für reine Zeitzonen-Anzeige. Für die tatsächliche Verschiebung zur UTC, die zu einem konkreten Geburtsdatum galt, gibt es die speziellen Fill-Keys timeshift, timeshift_hours, timeshift_hours_int, abbrev, dst u. a. — sie laden den Offset über /api/offset.php nach, sobald Ort + Datum + Zeit stehen.

timeshift: timeshift_hours: timeshift_hours_int: abbrev / dst: / at_utc: normalized:

Probiere: Wähle „München" als Ort, dann ändere das Datum auf einen Sommer- vs. Wintertag — der Offset wechselt zwischen +02:00 (CEST) und +01:00 (CET). Beim Spring-Forward (Berlin 1987-03-29 02:30 existierte nicht) springt normalized auf true.

<input type="text" name="tsplace" data-geoapi-place>
<input type="date" name="tsdate">
<input type="time" name="tstime">

<span data-geoapi-fill="timeshift"
      data-geoapi-timeshift-date="tsdate"
      data-geoapi-timeshift-time="tstime"></span>
Wann fragt das Widget den Offset ab? Nur wenn beides stimmt: ein Ort ist ausgewählt und Datum/Zeit sind gültig. Datum-Tippen ohne Ortsauswahl löst keinen API-Aufruf aus. Akzeptierte Datumsformate: ISO YYYY-MM-DD und deutsch DD.MM.YYYY (Tag/Monat 1- oder 2-stellig).

Wie geht es weiter?