Keyboard accessible Google map

The basic zoom and pan controls on embedded Google maps are not fully keyboard accessible as they do not allow keyboard users to pan the map.

This can be addressed by including the map via one of the scripts in the examples below.

Move focus to the map by pressing the Tab key, then press one of the Arrow keys on the keyboard to pan the map.

Example 1 - default controls

Code


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Keyboard access to Google maps</title>
<style>

#map_canvas {
    width: 500px;
    height: 400px;
}
</style>

<!--Google map library - add your API key to the URL-->
<script src="http://maps.googleapis.com/maps/api/js"></script>

<script>
(function(){

    if(!window.visionaustralia) window.visionaustralia = {};

    visionaustralia.addmap = function(domid,lat,lng,zo){

        function init() {
            var map_canvas = document.getElementById(domid);
            var map_options = {
                center: new google.maps.LatLng(lat,lng),
                zoom: zo,
                mapTypeId: google.maps.MapTypeId.
ROADMAP            }
            var map = new google.maps.Map(map_canvas, map_options);

            var hPan = Math.floor(map_canvas.offsetHeight/3);
            var wPan = Math.floor(map_canvas.offsetWidth/3);

            map_canvas.setAttribute("tabindex","0");

            map_canvas.addEventListener("focus", function(ev){this.style.outline = "2px solid #4D8FFD"});
            map_canvas.addEventListener("blur", function(ev){this.style.outline = "0"});

            map_canvas.addEventListener("keydown", function(ev){

                //exit if Ctrl or Alt is pressed
                //this allows users to scroll the page by pressing e.g. Ctrl + Arrow
                if(ev.ctrlKey || ev.altKey) return;

                var key = ev.key.toLowerCase();

                if(key==="+" || key=== "add"){
                    map.setZoom(map.getZoom() + 1);
                }else if(key==="-" || key==="subtract"){
                    map.setZoom(map.getZoom() - 1);
                }else if(key==="arrowup" || key==="up"){
                    map.panBy(0, wPan);
                }else if(key==="arrowdown" || key==="down"){
                    map.panBy(0, -wPan);
                }else if(key==="arrowleft" || key==="left"){
                    map.panBy(-hPan, 0);
                }else if(key==="arrowright" || key==="right"){
                    map.panBy(hPan, 0);
                }else if(key==="escape" || key==="esc"){
                    map.setZoom(zo);
                    map.setCenter({lat: lat, lng: lng});
                }else{
                    return
                }
                ev.preventDefault();
            });

        }//end init

        google.maps.event.addDomListener(window, 'load', init);

    }//end addmap
})();
</script>
</head>
<body>

<p>content....</p>
<div id="map_canvas"></div>
<p>.... other content</p>

<script>
//"map_canvas" is the id of the div that will contain the map. The 3 parameters after are: latitude, longitude and zoom level
visionaustralia.addmap("map_canvas",45.6,4.9,8);
</script>

</body>
</html>


Example 2 - custom controls

This example replaces the default controls with a custom SVG control.

Press the Tab key to move focus to a control, then press the Enter key to activate it.

To pan the map, move focus to one of the zoom controls, then press an Arrow key on the keyboard.

Code

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Keyboard access to Google maps - custom controls</title>
<style>

#map_canvas_2 {
    width: 500px;
    height: 400px;
}

</style>
<!--add Google map library - add your API key to the URL---->
<script src="http://maps.googleapis.com/maps/api/js"></script>

<!--add keybpard access script-->
<script>
(function(){

if(!window.visionaustralia) window.visionaustralia = {};

visionaustralia.addmapII = function(domid,lat,lng,zo){

    var map;

    function init() {
        var map_canvas = document.getElementById(domid);
        var map_options = {
            center: new google.maps.LatLng(lat,lng),
            zoom: zo,
            mapTypeId: google.maps.MapTypeId.ROADMAP,
            disableDefaultUI: true
        }
        map = new google.maps.Map(map_canvas, map_options);

        var hPan = Math.floor(map_canvas.offsetHeight/3);
        var wPan = Math.floor(map_canvas.offsetWidth/3);

        var div = document.createElement("div");
        div.setAttribute("style","position:absolute;border:none;outline:none;padding:0px;margin:0px 6px 0px 0px;");
        div.innerHTML ='<svg height="60" width="60" viewBox="0,0,102,102" aria-hidden="true" focusable="false"><g class="yang" style="cursor:pointer"><circle cx="51" cy="51" r="50" fill="white" style="stroke-width:1px;stroke:black"/><circle cx="51" cy="76" r="12" fill="black"/><path d="M 44,74 h14 v4 h-14 z" fill="white"/></g><g class="yin" style="cursor:pointer"><path d="M 51,1 C 65,1 76,12 76,26 76,40 65,51 51,51 37,51 26,62 26,76 26,90 37,101 51,101 C 23,101 1,79 1,51 1,23 23,1 51,1" fill="black" stroke="black"/><circle cx="51" cy="26" r="12" fill="white" stroke="black"/><path d="M 43,24 h6 v-6 h4 v6 h6 v4 h-6 v6 h-4 v-6 h-6 z" fill="black"/></g></svg>';

        var yin = div.getElementsByClassName("yin")[0];
        var yang = div.getElementsByClassName("yang")[0];

        var bStyl = "position: absolute !important;clip: rect(1px, 1px, 1px, 1px);padding: 0 !important;border: 0 !important;height: 1px !important;width: 1px !important;overflow: hidden";

        var yinB = document.createElement("button");
        yinB.setAttribute("aria-label","Zoom out");
        yinB.setAttribute("style",bStyl);

        var yangB = document.createElement("button");
        yangB.setAttribute("aria-label","Zoom in");
        yangB.setAttribute("style",bStyl);

        div.appendChild(yinB);
        div.appendChild(yangB);

        map.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(div);

        var arr,i,el;
        arr = [yin,yinB];
        for(i=0;el=arr[i];i++){
            el.addEventListener('click', function(ev) {
                map.setZoom(map.getZoom() + 1);
                ev.stopPropagation();
                ev.preventDefault();
            });
        }
        arr = [yang,yangB];
        for(i=0;el=arr[i];i++){
            el.addEventListener('click', function(ev) {
                map.setZoom(map.getZoom() - 1);
                ev.stopPropagation();
                ev.preventDefault();
            });
        }

        yinB.addEventListener('focus', function() {
            //IE wants this done everytime(?)
            var yins = yin.getElementsByTagName("*");
            yins[0].setAttribute("fill","yellow");
            yins[0].setAttribute("stroke-width","1");
            yins[1].setAttribute("stroke-width","1");
        });
        yinB.addEventListener('blur', function() {
            var yins = yin.getElementsByTagName("*");
            yins[0].setAttribute("fill","black");
            yins[0].setAttribute("stroke-width","0");
            yins[1].setAttribute("stroke-width","0");
        });
        yangB.addEventListener('focus', function() {
            var yangs = yang.getElementsByTagName("*");
            yangs[0].setAttribute("fill","yellow");
        });
        yangB.addEventListener('blur', function() {
            var yangs = yang.getElementsByTagName("*");
            yangs[0].setAttribute("fill","white");
        });

        arr = [yinB,yangB];
        for(i=0;el=arr[i];i++){
            el.addEventListener("keydown", function(ev){
                var key = ev.keyCode || ev.which;
                if(key==40) {//down
                    map.panBy(0, wPan);
                }else if(key==38){//up
                    map.panBy(0, -wPan);
                }else if(key==37){//left
                    map.panBy(-hPan, 0);
                }else if(key==39){//right
                    map.panBy(hPan, 0);
                }else if(key==61 || key== 107){
                    map.setZoom(map.getZoom() + 1);
                }else if(key==173 || key== 109){
                    map.setZoom(map.getZoom() - 1);
                }else{
                    return;
                }
                ev.stopPropagation();
                ev.preventDefault();
            });
        }
    }//end init

    google.maps.event.addDomListener(window, 'load', init);

}//end addmap
})();
</script>
</head>
<body>

<p>content....</p>
<div id="map_canvas_2"></div>
<p>.... other content</p>

<script>
//"map_canvas" is the id of the div that will contain the map and the 3 parameters after are: latitude, longitude and zoom level
visionaustralia.addmapII("map_canvas_2",35.6,117.0,10);
</script>

</body>
</html>

Historical note

Both pan and zoom controls on Google maps used to inaccessible as they were implemented as <div> elements without tabindex attributes or key event handlers. The script on this page addressed the issue by adding attributes and event handlers.
Now the zoom controls are implemented as <button> elements and therefore only the pan functionality needs to be added.

Terms of Use

Developed by Pierre Frederiksen. Pierre is a Principal Technical Consultant at Vision Australia

This software is being provided "as is", without any express or implied warranty. In particular, Vision Australia does not make any representation or warranty of any kind concerning the reliability, quality, or merchantability of this software or its fitness for any particular purpose. additionally, Vision Australia does not guarantee that use of this software will ensure the accessibility of your web content or that your web content will comply with any specific web accessibility standard.

Creative commons licence - logo
This work is licensed under a Creative Commons License



Print Print larger font