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