block by ThomasG77 0b99013795f76699c5c9a0d7daf4411e

Leaflet demo to use autocompletion with French GeoAPI and Leaflet.js library http://bl.ocks.org/ThomasG77/0b99013795f76699c5c9a0d7daf4411e

Full Screen

index.html

<!DOCTYPE html><html lang="en">
<head>
    <meta charset="utf-8">
    <title>Vanilla JavaScript autoComplete with French GeoAPI endpoint with Leaflet (adapted from my own OpenLayers sample)</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="auto-complete.css">
    <script src=""></script>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" />
    <style>
      html, body {
        height: 100%;
        padding: 0;
        margin: 0;
      }
      #map {
        /* configure the size of the map */
        width: 100%;
        height: 100%;
      }
    </style>
    <script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet.js"></script>
</head>
<body>
    <form onsubmit="return false;" style="border-top: 1px solid #eee;border-bottom:1px solid #eee;background:#fafafa;margin:30px 0;padding:20px 10px;text-align:center">
        <input id="places-search" autofocus type="text" name="q" placeholder="Communes ..." style="width:100%;max-width:600px;outline:0">
    </form>

    <div id="map" class="map"></div>
    <script src="auto-complete.js"></script>
    <script>

      var OpenStreetMap_France = L.tileLayer('//{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png', {
        maxZoom: 20,
        attribution: '&copy; Openstreetmap France | &copy; <a href="//www.openstreetmap.org/copyright">OpenStreetMap</a>'
      });

      var map = L.map('map').setView({lon: 1.87528, lat: 46.60611}, 5);

      map.addLayer(OpenStreetMap_France);

      var demo_with_map = new autoComplete({
        selector: '#places-search',
        minChars: 2,
        source: function(term, response) {
          fetch('https://geo.api.gouv.fr/communes?boost=population&fields=centre&nom=' + term)
            .then(function(response) {
              return response.text();
            }).then(function(body) {
              var json = JSON.parse(body);
              var new_json = json.map(function(el) {
                return {
                  label: el.nom + ' (' + el.code + ')',
                  value: el.code,
                  lat: el.centre.coordinates[1],
                  lon: el.centre.coordinates[0],
                  boundingbox: null
                }
              })
              response(new_json);
            });
        },
        renderItem: function(item, search) {
          search = search.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
          var re = new RegExp("(" + search.split(' ').join('|') + ")", "gi");
          var optional_bbox_attribute = '';
          if (item.boundingbox) {
            var bbox = [item.boundingbox[2], item.boundingbox[0], item.boundingbox[3], item.boundingbox[1]];
            var optional_bbox_attribute = 'data-bbox="' + bbox.join(',') + '" ';
          }
          return '<div class="autocomplete-suggestion" ' + optional_bbox_attribute +
            'data-lon="' + item.lon + '" data-lat="' + item.lat +
            '" data-val="' + item.label + '">' +
            item.label.replace(re, "<b>$1</b>") +
            '</div>';
        },
        onSelect: function(e, term, item) {
          if (item.getAttribute('data-bbox') && (item.getAttribute('data-bbox').split(',')).length > 0) {
            var extent = item.getAttribute('data-bbox').split(',');
            if (extent.length > 0) {
              extent = extent.map(function(el) {
                return Number(el);
              });
            }
            var bounds = [[extent[1], extent[0]], [extent[3], extent[2]]];

            // zoom the map to the bounds
            map.fitBounds(bounds);
          } else {
            var lat = Number(item.getAttribute('data-lat'));
            var lon = Number(item.getAttribute('data-lon'));
            map.setView(L.latLng(lat, lon), 12);
          }
        }
      });

    </script>
</body>
</html>

auto-complete.css

.autocomplete-suggestions {
    text-align: left; cursor: default; border: 1px solid #ccc; border-top: 0; background: #fff; box-shadow: -1px 1px 3px rgba(0,0,0,.1);

    /* core styles should not be changed */
    position: absolute; display: none; z-index: 9999; max-height: 254px; overflow: hidden; overflow-y: auto; box-sizing: border-box;
}
.autocomplete-suggestion { position: relative; padding: 0 .6em; line-height: 23px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 1.02em; color: #333; }
.autocomplete-suggestion b { font-weight: normal; color: #1f8dd6; }
.autocomplete-suggestion.selected { background: #f0f0f0; }

auto-complete.js

/*
    JavaScript autoComplete v1.0.3
    Copyright (c) 2014 Simon Steinberger / Pixabay
    GitHub: https://github.com/Pixabay/JavaScript-autoComplete
    License: http://www.opensource.org/licenses/mit-license.php
*/

var autoComplete = (function(){
    // "use strict";
    function autoComplete(options){
        if (!document.querySelector) return;

        // helpers
        function hasClass(el, className){ return el.classList ? el.classList.contains(className) : new RegExp('\\b'+ className+'\\b').test(el.className); }

        function addEvent(el, type, handler){
            if (el.attachEvent) el.attachEvent('on'+type, handler); else el.addEventListener(type, handler);
        }
        function removeEvent(el, type, handler){
            // if (el.removeEventListener) not working in IE11
            if (el.detachEvent) el.detachEvent('on'+type, handler); else el.removeEventListener(type, handler);
        }
        function live(elClass, event, cb, context){
            addEvent(context || document, event, function(e){
                var found, el = e.target || e.srcElement;
                while (el && !(found = hasClass(el, elClass))) el = el.parentElement;
                if (found) cb.call(el, e);
            });
        }

        var o = {
            selector: 0,
            source: 0,
            minChars: 3,
            delay: 150,
            cache: 1,
            menuClass: '',
            renderItem: function (item, search){
                // escape special characters
                search = search.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
                var re = new RegExp("(" + search.split(' ').join('|') + ")", "gi");
                return '<div class="autocomplete-suggestion" data-val="' + item + '">' + item.replace(re, "<b>$1</b>") + '</div>';
            },
            onSelect: function(e, term, item){}
        };
        for (var k in options) { if (options.hasOwnProperty(k)) o[k] = options[k]; }

        // init
        var elems = typeof o.selector == 'object' ? [o.selector] : document.querySelectorAll(o.selector);
        for (var i=0; i<elems.length; i++) {
            var that = elems[i];

            // create suggestions container "sc"
            that.sc = document.createElement('div');
            that.sc.className = 'autocomplete-suggestions '+o.menuClass;

            that.autocompleteAttr = that.getAttribute('autocomplete');
            that.setAttribute('autocomplete', 'off');
            that.cache = {};
            that.last_val = '';

            that.updateSC = function(resize, next){
                var rect = that.getBoundingClientRect();
                that.sc.style.left = rect.left + (window.pageXOffset || document.documentElement.scrollLeft) + 'px';
                that.sc.style.top = rect.bottom + (window.pageYOffset || document.documentElement.scrollTop) + 1 + 'px';
                that.sc.style.width = rect.right - rect.left + 'px'; // outerWidth
                if (!resize) {
                    that.sc.style.display = 'block';
                    if (!that.sc.maxHeight) { that.sc.maxHeight = parseInt((window.getComputedStyle ? getComputedStyle(that.sc, null) : that.sc.currentStyle).maxHeight); }
                    if (!that.sc.suggestionHeight) that.sc.suggestionHeight = that.sc.querySelector('.autocomplete-suggestion').offsetHeight;
                    if (that.sc.suggestionHeight)
                        if (!next) that.sc.scrollTop = 0;
                        else {
                            var scrTop = that.sc.scrollTop, selTop = next.getBoundingClientRect().top - that.sc.getBoundingClientRect().top;
                            if (selTop + that.sc.suggestionHeight - that.sc.maxHeight > 0)
                                that.sc.scrollTop = selTop + that.sc.suggestionHeight + scrTop - that.sc.maxHeight;
                            else if (selTop < 0)
                                that.sc.scrollTop = selTop + scrTop;
                        }
                }
            }
            addEvent(window, 'resize', that.updateSC);
            document.body.appendChild(that.sc);

            live('autocomplete-suggestion', 'mouseleave', function(e){
                var sel = that.sc.querySelector('.autocomplete-suggestion.selected');
                if (sel) setTimeout(function(){ sel.className = sel.className.replace('selected', ''); }, 20);
            }, that.sc);

            live('autocomplete-suggestion', 'mouseover', function(e){
                var sel = that.sc.querySelector('.autocomplete-suggestion.selected');
                if (sel) sel.className = sel.className.replace('selected', '');
                this.className += ' selected';
            }, that.sc);

            live('autocomplete-suggestion', 'mousedown', function(e){
                if (hasClass(this, 'autocomplete-suggestion')) { // else outside click
                    var v = this.getAttribute('data-val');
                    that.value = v;
                    o.onSelect(e, v, this);
                    that.sc.style.display = 'none';
                }
            }, that.sc);

            that.blurHandler = function(){
                try { var over_sb = document.querySelector('.autocomplete-suggestions:hover'); } catch(e){ var over_sb = 0; }
                if (!over_sb) {
                    that.last_val = that.value;
                    that.sc.style.display = 'none';
                    setTimeout(function(){ that.sc.style.display = 'none'; }, 350); // hide suggestions on fast input
                } else if (that !== document.activeElement) setTimeout(function(){ that.focus(); }, 20);
            };
            addEvent(that, 'blur', that.blurHandler);

            var suggest = function(data){
                var val = that.value;
                that.cache[val] = data;
                if (data.length && val.length >= o.minChars) {
                    var s = '';
                    for (var i=0;i<data.length;i++) s += o.renderItem(data[i], val);
                    that.sc.innerHTML = s;
                    that.updateSC(0);
                }
                else
                    that.sc.style.display = 'none';
            }

            that.keydownHandler = function(e){
                var key = window.event ? e.keyCode : e.which;
                // down (40), up (38)
                if ((key == 40 || key == 38) && that.sc.innerHTML) {
                    var next, sel = that.sc.querySelector('.autocomplete-suggestion.selected');
                    if (!sel) {
                        next = (key == 40) ? that.sc.querySelector('.autocomplete-suggestion') : that.sc.childNodes[that.sc.childNodes.length - 1]; // first : last
                        next.className += ' selected';
                        that.value = next.getAttribute('data-val');
                    } else {
                        next = (key == 40) ? sel.nextSibling : sel.previousSibling;
                        if (next) {
                            sel.className = sel.className.replace('selected', '');
                            next.className += ' selected';
                            that.value = next.getAttribute('data-val');
                        }
                        else { sel.className = sel.className.replace('selected', ''); that.value = that.last_val; next = 0; }
                    }
                    that.updateSC(0, next);
                    return false;
                }
                // esc
                else if (key == 27) { that.value = that.last_val; that.sc.style.display = 'none'; }
                // enter
                else if (key == 13 || key == 9) {
                    var sel = that.sc.querySelector('.autocomplete-suggestion.selected');
                    if (sel && that.sc.style.display != 'none') { o.onSelect(e, sel.getAttribute('data-val'), sel); setTimeout(function(){ that.sc.style.display = 'none'; }, 20); }
                }
            };
            addEvent(that, 'keydown', that.keydownHandler);

            that.keyupHandler = function(e){
                var key = window.event ? e.keyCode : e.which;
                if (!key || (key < 35 || key > 40) && key != 13 && key != 27) {
                    var val = that.value;
                    if (val.length >= o.minChars) {
                        if (val != that.last_val) {
                            that.last_val = val;
                            clearTimeout(that.timer);
                            if (o.cache) {
                                if (val in that.cache) { suggest(that.cache[val]); return; }
                                // no requests if previous suggestions were empty
                                for (var i=1; i<val.length-o.minChars; i++) {
                                    var part = val.slice(0, val.length-i);
                                    if (part in that.cache && !that.cache[part].length) { suggest([]); return; }
                                }
                            }
                            that.timer = setTimeout(function(){ o.source(val, suggest) }, o.delay);
                        }
                    } else {
                        that.last_val = val;
                        that.sc.style.display = 'none';
                    }
                }
            };
            addEvent(that, 'keyup', that.keyupHandler);

            that.focusHandler = function(e){
                that.last_val = '\n';
                that.keyupHandler(e)
            };
            if (!o.minChars) addEvent(that, 'focus', that.focusHandler);
        }

        // public destroy method
        this.destroy = function(){
            for (var i=0; i<elems.length; i++) {
                var that = elems[i];
                removeEvent(window, 'resize', that.updateSC);
                removeEvent(that, 'blur', that.blurHandler);
                removeEvent(that, 'focus', that.focusHandler);
                removeEvent(that, 'keydown', that.keydownHandler);
                removeEvent(that, 'keyup', that.keyupHandler);
                if (that.autocompleteAttr)
                    that.setAttribute('autocomplete', that.autocompleteAttr);
                else
                    that.removeAttribute('autocomplete');
                document.body.removeChild(that.sc);
                that = null;
            }
        };
    }
    return autoComplete;
})();

(function(){
    if (typeof define === 'function' && define.amd)
        define('autoComplete', function () { return autoComplete; });
    else if (typeof module !== 'undefined' && module.exports)
        module.exports = autoComplete;
    else
        window.autoComplete = autoComplete;
})();

demo-ban-form-only-alternate.html

<!DOCTYPE html>
<html lang="en">

<head>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.min.css">
    <style type="text/css">
      div.myform > label {
        color:  rgba(255,122,122,1);
        display:block;
      }
      div.myform > label > input {
        height: 1.5rem;
        width: auto;
        margin: 5px 0 0px 10px;
        padding: 0 0.5rem 0 0.5rem;
        box-sizing: border-box;
        -moz-box-sizing: border-box;
        -webkit-box-sizing: border-box;
        font-size: 1rem;
        text-overflow: ellipsis;
        color: rgba(255, 122, 122, 0.3);
        outline: none;
        border-radius: 3rem;
        border: 0.05rem solid rgba(255, 122, 122, 0.5);
        background-image: none;
        /*display: block;*/
        background-color: #fff;
        transition: all 0.4s ease;
        -webkit-transition: all -webkit-transform 0.4s ease;
      }
    </style>
</head>

<body>
    <div class="autoComplete_wrapper">
        <input id="autoComplete" type="search" dir="ltr" spellcheck=false autocorrect="off" autocomplete="off" autocapitalize="off">
    </div>
    <div class="myform">
        <label>Adresse<input id="adress" class="normal-input"></label>
        <label>Code postal<input id="postcode"></label>
        <label>Code INSEE<input id="citycode"></label>
        <label>Nom commune<input id="city"></label>
        <label>Longitude<input id="lon"></label>
        <label>Latitude<input id="lat"></label>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/autoComplete.min.js"></script>
    <script>
        var myobject = []
        const adress = document.querySelector('#adress')
        const postcode = document.querySelector('#postcode')
        const citycode = document.querySelector('#citycode')
        const city = document.querySelector('#city')
        const lon = document.querySelector('#lon')
        const lat = document.querySelector('#lat')
        const autoCompleteJS = new autoComplete({
            selector: "#autoComplete",
            placeHolder: "Recherche adresse...",
            data: {
                src: async () => {
                   const term = document.querySelector('#autoComplete').value;
                   if (term) {
                     const response = await fetch(`https://api-adresse.data.gouv.fr/search/?q=${term}`)
                     const json = await response.json()
                     myobject = json.features.map(function(el) {
                        return {
                          label: el.properties.label,
                          value: el.properties.label,
                          lat: el.geometry.coordinates[1],
                          lon: el.geometry.coordinates[0],
                          housenumber: el.properties.housenumber,
                          name: el.properties.name,
                          postcode: el.properties.postcode,
                          citycode: el.properties.citycode,
                          city: el.properties.city,
                          context: el.properties.context,
                          type: el.properties.type,
                          street: el.properties.street,
                          boundingbox: null
                        }
                      })
                      adresses = myobject.map(el => el.value)
                      console.log(adresses);
                   } else {
                     adresses = []
                   }
                  // ... other code
                  return adresses
                },
                // src: ["Sauce - Thousand Island", "Wild Boar - Tenderloin", "Goat - Whole Cut"],
                cache: false,
            },
            resultsList: {
                element: (list, data) => {
                    if (!data.results.length) {
                        // Create "No Results" message element
                        const message = document.createElement("div");
                        // Add class to the created element
                        message.setAttribute("class", "no_result");
                        // Add message text content
                        message.innerHTML = `<span>Found No Results for "${data.query}"</span>`;
                        // Append message element to the results list
                        list.prepend(message);
                    }
                },
                noResults: true,
            },
            resultItem: {
                highlight: true
            },
            events: {
                input: {
                    selection: (event) => {
                        const selection = event.detail.selection.value;
                        autoCompleteJS.input.value = selection;
                        var result = myobject.find(el => el.value == event.detail.selection.value)
                        if (result) {
                          adress.value = result.name
                          postcode.value = result.postcode
                          citycode.value = result.citycode
                          city.value = result.city
                          lon.value = result.lon
                          lat.value = result.lat
                        } else {
                          adress.value = ''
                          postcode.value = ''
                          citycode.value = ''
                          city.value = ''
                          lon.value = ''
                          lat.value = ''
                        }

                    }
                }
            }
        });
    </script>
</body>

demo-ban-leaflet-photon.html

<!DOCTYPE html><html lang="en">
<head>
    <meta charset="utf-8">
    <title>Autcompletion avec Leaflet (avec le plugin leaflet-photon) en utilisant la Base Adresse Nationale (BAN)</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://raw.githack.com/komoot/leaflet.photon/master/leaflet.photon.css">
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" />
    <style>
      html, body {
        height: 100%;
        padding: 0;
        margin: 0;
      }
      #map {
        /* configure the size of the map */
        width: 100%;
        height: 100%;
      }
      ul.photon-autocomplete {
        position: absolute;
        background-color: white;
        z-index: 1000;
        box-shadow: 0 4px 9px #999999;
        display: none;
        padding-inline-start:  0px;
      }

      .photon-autocomplete li span {
        float: right;
        font-size: 12px;
        font-style: italic;
        font-weight: normal;
      }
    </style>
    <script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet.js"></script>
</head>
<body>
  <div id="map" class="map"></div>
    <script src="https://raw.githack.com/komoot/leaflet.photon/master/leaflet.photon.js"></script>
    <script>

      var OpenStreetMap_France = L.tileLayer('//{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png', {
        maxZoom: 20,
        attribution: '&copy; Openstreetmap France | &copy; <a href="//www.openstreetmap.org/copyright">OpenStreetMap</a>'
      });

      var map = L.map('map').setView({lon: 1.87528, lat: 46.60611}, 5);

      map.addLayer(OpenStreetMap_France);
      
      var formatResult = function(feature, el) {
          var title = document.createElement('strong');
          el.appendChild(title);
          var detailsContainer = document.createElement('small');
          el.appendChild(detailsContainer);
          var details = [];
          title.innerHTML = feature.properties.label || feature.properties.name;
          var types = {
              housenumber: 'numéro',
              street: 'rue',
              locality: 'lieu-dit',
              municipality: 'commune'
          };
          if (types[feature.properties.type]) {
            var spanType = document.createElement('span');
            spanType.className = 'type';
            title.appendChild(spanType);
            spanType.innerHTML = types[feature.properties.type];
          }
          if (feature.properties.city && feature.properties.city !== feature.properties.name) {
            details.push(feature.properties.city);
          }
          if (feature.properties.context) {
            details.push(feature.properties.context);
          }
          detailsContainer.innerHTML = details.join(', ');
      };
      // Si vous avez besoin d'effectuer des opérations après la sélection
      function myHandler(geojson) {
          console.debug(geojson);
      };
      var photonOptions = {
        resultsHandler: myHandler,
        placeholder: 'Adresse...',
        position: 'topright',
        feedbackEmail: null,
        lang: 'fr',
        url: 'https://api-adresse.data.gouv.fr/search/?',
        formatResult: formatResult
      }

      var searchControl = L.control.photon(photonOptions);
      searchControl.addTo(map);

    </script>
</body>
</html>

demo-ban-openlayers.html


<!DOCTYPE html>
<html>
<head>
<!--
  Copyright (c) 2017-2018 Jean-Marc VIGLINO,
  released under CeCILL-B (french BSD like) licence: //www.cecill.info/
-->
  <title>ol-ext: Search BAN control</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

  <meta name="description" content="Control to add a grid reference to a map." />
  <meta name="keywords" content="ol3, control, search, BAN, french, places, autocomplete" />

  <!-- Openlayers -->
  <link rel="stylesheet" href="https://openlayers.org/en/latest/css/ol.css" />
  <script type="text/javascript" src="https://openlayers.org/en/latest/build/ol.js"></script>
  <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL,Object.assign"></script>
  
  <!-- ol-ext -->
  <link rel="stylesheet" href="https://viglino.github.io/ol-ext/dist/ol-ext.css" />
  <script type="text/javascript" src="https://viglino.github.io/ol-ext/dist/ol-ext.js"></script>
  <!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer -->
  <script src="https://unpkg.com/elm-pep"></script>

  <style>
      html, body {
        height: 100%;
        padding: 0;
        margin: 0;
      }
      #map {
        /* configure the size of the map */
        width: 100%;
        height: 100%;
      }
    .ol-search ul {
      color: #333;
      font-size:0.85em;
      max-width: 21em;
    }
    .ol-search ul i {
      display: block;
      color: #333;
      font-size:0.85em;
    }
    .ol-search ul li.copy {
      display: none;
    }
    .info a img {
      display: inline-block;
      height: 100px;
      margin: .5em;
    }
  </style>
</head>
<body >
  <!-- DIV pour la carte -->
  <div id="map"></div>
  
<script type="text/javascript">
  // Layers
  var layers = [ 
    new ol.layer.Tile({ title: 'OSM', source: new ol.source.OSM(), visible: false }),
    new ol.layer.Geoportail({
      layer: 'GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2', 
    }), new ol.layer.Geoportail({
      layer: 'CADASTRALPARCELS.PARCELLAIRE_EXPRESS',
      visible: false
    })
  ];

  // The map
  var map = new ol.Map ({
    target: 'map',
    view: new ol.View ({
      zoom: 5,
      center: [166326, 5992663]
    }),
    interactions: ol.interaction.defaults({ altShiftDragRotate:false, pinchRotate:false }),
    layers: layers
  });
  map.addControl(new ol.control.LayerSwitcher());

  // Set the control grid reference
  var search = new ol.control.SearchBAN({
    reverse:true,
    placeholder: 'Adresse',
    typing: 150,
    position: true	// Search, with priority to geo position
  });
  map.addControl(search);

  // Select feature when click on the reference index
  search.on('select', function(e) {
    map.getView().animate({
      center:e.coordinate,
      zoom: Math.max (map.getView().getZoom(),16)
    });
  });

</script>
  
</body>
</html>