none
Infobox element click events don't bubble/propagate up RRS feed

  • Question

  • Using Maps V8 and an Infobox populated with HTML content (via description, not htmlContent). Click events triggered on any HTML element are not propagating past the div.Infobox element. And since the div.Infobox element is created/destroyed rather frequently (on setMap and setLocation), it is very difficult to keep bound, particularly since there can be a delay after calling setMap or setLocation before the elements are created and ready to be bound. It's also a mess if you use multiple Infoboxes.

    I'm guessing the culprit is the Infobox click event itself probably has an event.stopPropagation(), perhaps to prevent the map click event from being triggered as well? If so, some filtering/exclusion should be added for targets inside Infoboxes. mouseenter and mouseleave events on elements still propagate normally, however. I have not tested other events.

    So to summarize:

    1) Need click event to propagate so it can be delegated instead of directly bound

    2) Need Infobox.setMap() to trigger some kind of domready event so events can be bound to included content without having to hack together timeout checks

    var map = new Microsoft.Maps.Map(document.getElementById('myMap'), {
        credentials: 'Your Bing Maps Key',
        center: new Microsoft.Maps.Location(47.60357, -122.32945)
    });
    var infobox = new Microsoft.Maps.Infobox(map.getCenter(), { title: 'Map Center', description: 'Seattle <div id="div_bubble"><button id="button_click">Click me</button></div>' });
    infobox.setMap(map);
    setTimeout(function() {
    	var button = document.getElementById('button_click');
    	button.addEventListener('click', function(evt) { console.log('button click for target:'); console.log(evt.target); }, false);	
    	var div = document.getElementById('div_bubble');
    	div.addEventListener('click', function(evt) { console.log('div click for target:'); console.log(evt.target); }, false);
    	document.addEventListener('click', function(evt) { console.log('document click for target:'); console.log(evt.target); }, false);
    }, 5000);
    
    


    Friday, October 28, 2016 6:25 PM

Answers

  • I have given a lot of thought around this. Going to the extent that you have requested leads me to suggest taking a look at CustomOverlays. Custom overlays fire an even once they are available in the DOM. Here is some documentation about these:

    https://msdn.microsoft.com/en-us/library/mt762871.aspx

    https://msdn.microsoft.com/en-us/library/mt762874.aspx

    Custom Overlays are pretty powerful. So much so I've started using them to prototype new features which the dev team has been able to easily add in to the base map control without needing any changes to integrate them in.

    That said, to keep things simple I have requested that the original event object that is fired be returned in the infoboxes event object. By doing this you can use the standard map event functionality and then look at the object the original event occurred on and check it's id or class property (which ever you want to use to uniquely identify your buttons). I think this would keep things fairly simple.  


    [Blog] [twitter] [LinkedIn]

    Friday, October 28, 2016 8:22 PM
  • To get custom overlays to be clickable you have the set the benethLabels option to false. Then any event you add directly to your html elements easier.

    Custom Overlays don't have any built in events this the error when adding a click event.


    [Blog] [twitter] [LinkedIn]

    Saturday, October 29, 2016 4:50 PM
  • This won't be done as all mouse events within the map area have to go through a lot of internal logic in the map. The reason for this is that the map uses HTML5 canvas for rendering majority of the data on the map. A canvas is essentially a glorified image. As such, to support clicking of shapes a who bunch of calculations are done to determine what was clicked, and if no shapes were clicked, then the map is considered to be clicked. You can however add traditional mouse events to the map div itself and they will fire in addition to the mouse events within the map API.

    [Blog] [twitter] [LinkedIn]

    Wednesday, November 2, 2016 7:56 PM

All replies

  • I have given a lot of thought around this. Going to the extent that you have requested leads me to suggest taking a look at CustomOverlays. Custom overlays fire an even once they are available in the DOM. Here is some documentation about these:

    https://msdn.microsoft.com/en-us/library/mt762871.aspx

    https://msdn.microsoft.com/en-us/library/mt762874.aspx

    Custom Overlays are pretty powerful. So much so I've started using them to prototype new features which the dev team has been able to easily add in to the base map control without needing any changes to integrate them in.

    That said, to keep things simple I have requested that the original event object that is fired be returned in the infoboxes event object. By doing this you can use the standard map event functionality and then look at the object the original event occurred on and check it's id or class property (which ever you want to use to uniquely identify your buttons). I think this would keep things fairly simple.  


    [Blog] [twitter] [LinkedIn]

    Friday, October 28, 2016 8:22 PM
  • Thanks for the quick reply Ricky. I just tried out the Custom Overlay and it is interesting though it seems to also suffer from the same propagation issue for the click event. Meaning that if I add a click event on an element higher in the tree outside of the layer it is never triggered. The workaround here is that I can at least add the layer with a static root element and delegate the events to it, so it may meet my needs.

    I was using the HTML Pushpin Overlay example as a test and then adding click event listeners on the document like before. They were squashed. Direct binding still works, of course.

    On a related note, trying to add an event handler to a custom overlay layer returns a fatal error. I guess it makes sense that it wouldn't work? but might want to squash the error:

    var map = new Microsoft.Maps.Map(document.getElementById('myMap'), {
        credentials: 'Your Bing Maps Key',
        center: new Microsoft.Maps.Location(40.25, -123.25),
        zoom: 8
    });
    var img;
    // Define custom constructor for the overlay 
    function TopographicOverlay(bounds, image) {
        this.bounds = bounds;
        this.image = image;
    }
    // set prototype to sub-class CustomOverlay
    TopographicOverlay.prototype = new Microsoft.Maps.CustomOverlay();
    // implement the onAdd method to set up DOM element, and use setHtmlElement bind it with the overlay
    TopographicOverlay.prototype.onAdd = function () {
        img = document.createElement('img');
        img.src = this.image;
        img.id = 'topographicOverlay';
        img.style.width = '100%';
        img.style.height = '100%';
        img.style.position = 'absolute';
        this.setHtmlElement(img);
    };
    // implement the onLoad method to perform custom operations of rendering the DOM element
    TopographicOverlay.prototype.onLoad = function () {
        repositionOverlay();
        Microsoft.Maps.Events.addHandler(map, 'viewchange', function () {
            repositionOverlay();
        });
    };
    var bounds = Microsoft.Maps.LocationRect.fromCorners(new Microsoft.Maps.Location(40.5, -123.5), new Microsoft.Maps.Location(40, -123));
    var imageSrc = 'https://bingmapsisdk.blob.core.windows.net/isdksamples/topographicMap.gif';
    // create an instance of the defined custom overlay 
    var overlay = new TopographicOverlay(bounds, imageSrc);
    // now we're ready to insert this custom overlay into map layers
    map.layers.insert(overlay);
    Microsoft.Maps.Events.addHandler(overlay, 'click', function () { highlight('layerClick'); });
    function repositionOverlay() {
        var topLeft = map.tryLocationToPixel(bounds.getNorthwest(), Microsoft.Maps.PixelReference.control);
        var bottomRight = map.tryLocationToPixel(bounds.getSoutheast(), Microsoft.Maps.PixelReference.control);
        img.style.left = topLeft.x + 'px';
        img.style.top = topLeft.y + 'px';
        img.style.width = (bottomRight.x - topLeft.x) + 'px';
        img.style.width = (bottomRight.x - topLeft.x) + 'px';
        img.style.height = (bottomRight.y - topLeft.y) + 'px';
    }
    

    Saturday, October 29, 2016 12:39 AM
  • To get custom overlays to be clickable you have the set the benethLabels option to false. Then any event you add directly to your html elements easier.

    Custom Overlays don't have any built in events this the error when adding a click event.


    [Blog] [twitter] [LinkedIn]

    Saturday, October 29, 2016 4:50 PM
  • By clickable I mean that it exhibits the same behavior as the Infobox: click events are not propagated past the custom layer. Meaning that it is not triggered any higher on the DOM tree, making event delegation more difficult.

    Not the end of the world, it just makes application development more tedious as you have to bind events to elements as they are created. I'd suggest allowing click events to propagate higher up the DOM tree, if possible. If not, OK.

    Thanks for your help.

    Wednesday, November 2, 2016 2:46 PM
  • This won't be done as all mouse events within the map area have to go through a lot of internal logic in the map. The reason for this is that the map uses HTML5 canvas for rendering majority of the data on the map. A canvas is essentially a glorified image. As such, to support clicking of shapes a who bunch of calculations are done to determine what was clicked, and if no shapes were clicked, then the map is considered to be clicked. You can however add traditional mouse events to the map div itself and they will fire in addition to the mouse events within the map API.

    [Blog] [twitter] [LinkedIn]

    Wednesday, November 2, 2016 7:56 PM