Typeform in Mapbox

by Dries De Roeck on December 25, 2017

For a project at Studio Dott, we were looking for ways to integrate a questionnaire (a typeform, ideally) inside of a map environment. This has been something I have been wanting to explore outside of this project context, and I have been longing to dive into mapbox again. I worked with their deprecated TileMill several moons ago, but was very willing to dive into their new tools.

Enter Mapbox-GL-JS. I followed a basic example to set things up, picked a nice styling and got going. My eventual goal was very basic, visualise some clickable points on a map. Each point would be linked to a typeform questionnaire.

I was very happy to find our that Mapbox-GL-JS allowed me to create that pretty much out of the box. I followed this tutorial to get going, with my super basic understanding of javascript I did manage to follow this super easily.

Get the typeform iframe

Once that was done, some magic was needed to add a Typeform form to a popup. Luckily Typeform allows to embed their forms in various ways. One of those is a ‘full page embed’ – the default code looks like this:

<html> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"> <title>Add your Typeform title here</title> <style type="text/css"> html{ margin: 0; height: 100%; overflow: hidden; } iframe{ position: absolute; left:0; right:0; bottom:0; top:0; border:0; } </style> </head> <body> <iframe id="typeform-full" width="100%" height="100%" frameborder="0" src="link_to_form"></iframe> <script type="text/javascript" src="https://embed.typeform.com/embed.js"></script> </body> </html>

I spotted some iframe code in there, which I figured would come in handy to paste into the mapbox code. So out of the typeform embed code, we only need the iframe part:

<iframe id="typeform-full" width="100%" height="100%" frameborder="0" src="link_to_form"></iframe>

So, once we have this – time to paste it in the mabox example.

Add links to geojson

What I did was create an extra property field in the geojson mapbox uses to create the markers on the map. I called it ‘form’ and pasted the iframe code there:

var geojson = {
  type: 'FeatureCollection',
  features: [{
    type: 'Feature',
    geometry: {
      type: 'Point',
      coordinates: [3.7287033, 51.082785]
    },
    properties: {
      title: 'This is point one',
      description: 'Desc point one',
      form:'<iframe id="typeform-full" width="600px" height="400px" frameborder="0" src="link_to_form1"></iframe>'
    }
  },
  {
    type: 'Feature',
    geometry: {
      type: 'Point',
      coordinates: [3.729, 51.084]
    },
    properties: {
      title: 'This is point two',
      description: 'Desc point two',
      form:'<iframe id="typeform-full" width="600px" height="400px" frameborder="0" src="link_to_form2"></iframe>'
    }
  }]
};

This allows me to quickly hack together multiple question forms linked to multiple points on the map.

Fancification

As you can see in the code above, I tweaked some of the variables of the iframe size in order for it to display better in the popups. There are probably other/better ways to do this, but for the Proof Of Concept I was creating, this was good enough.

The final code

For completeness (and my own reference) this is the final proof of concept code. All inline styling & scripting html hackery. I took out style, access token & typeform links for the example’s sake.

Working version of this is online here

<!DOCTYPE html>
<html>
<head>
    <meta charset='utf-8' />
    <title>A test for Ghent</title>
    <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
    <script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.42.2/mapbox-gl.js'></script>
    <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.42.2/mapbox-gl.css' rel='stylesheet' />
    <style>
      body { margin:0; padding:0; }
      #map { position:absolute; top:0; bottom:0; width:100%; }

      .marker {
      background-image: url('mapbox-icon.png');
      background-size: cover;
      width: 50px;
      height: 50px;
      border-radius: 50%;
      cursor: pointer;
      }

      .mapboxgl-popup-content {
        text-align: center;
        font-family: 'Open Sans', sans-serif;
      }
    </style>  
</head>

<body>

<div id='map'>


</div>
<script>

mapboxgl.accessToken = 'token';
var map = new mapboxgl.Map({
    container: 'map', // container id
    style: 'style_link', // stylesheet location
    center: [3.7287033, 51.082785], // starting position [lng, lat]
    pitch: 45,
    bearing: -17.6,
    zoom: 17 // starting zoom
});

map.addControl(new mapboxgl.NavigationControl());

var geojson = {
  type: 'FeatureCollection',
  features: [{
    type: 'Feature',
    geometry: {
      type: 'Point',
      coordinates: [3.7287033, 51.082785]
    },
    properties: {
      title: 'This is point one',
      description: 'Desc point one',
      form:'<iframe id="typeform-full" width="600px" height="400px" frameborder="0" src="form1"></iframe>'
    }
  },
  {
    type: 'Feature',
    geometry: {
      type: 'Point',
      coordinates: [3.729, 51.084]
    },
    properties: {
      title: 'This is point two',
      description: 'Desc point two',
      form:'<iframe id="typeform-full" width="600px" height="400px" frameborder="0" src="form2"></iframe>'
    }
  }]
};

// add markers to map
geojson.features.forEach(function(marker) {

  // create a HTML element for each feature
  var el = document.createElement('div');
  el.className = 'marker';

  // make a marker for each feature and add to the map
  new mapboxgl.Marker(el)
    .setLngLat(marker.geometry.coordinates)
    .setPopup(new mapboxgl.Popup({ offset: 25 }) // add popups
    .setHTML(marker.properties.title + '</h3><p>' + marker.properties.form + '</p>')
  )
    .addTo(map);

    });

    // The 'building' layer in the mapbox-streets vector source contains building-height
    // data from OpenStreetMap.
    map.on('load', function() {
        // Insert the layer beneath any symbol layer.
        var layers = map.getStyle().layers;

        var labelLayerId;
        for (var i = 0; i < layers.length; i++) {
            if (layers[i].type === 'symbol' && layers[i].layout['text-field']) {
                labelLayerId = layers[i].id;
                break;
            }
        }

        map.addLayer({
            'id': '3d-buildings',
            'source': 'composite',
            'source-layer': 'building',
            'filter': ['==', 'extrude', 'true'],
            'type': 'fill-extrusion',
            'minzoom': 10,
            'paint': {
                'fill-extrusion-color': '#aaa',

                // use an 'interpolate' expression to add a smooth transition effect to the
                // buildings as the user zooms in
                'fill-extrusion-height': [
                    "interpolate", ["linear"], ["zoom"],
                    15, 0,
                    15.05, ["get", "height"]
                ],
                'fill-extrusion-base': [
                    "interpolate", ["linear"], ["zoom"],
                    15, 0,
                    15.05, ["get", "min_height"]
                ],
                'fill-extrusion-opacity': .6
            }
        }, labelLayerId);
    });


</script>

</body>
</html>

 

Leave your comment

Required.

Required. Not published.

If you have one.