I have a website with a google map filled with multiple markers. Each marker stands for an event on a festival. Since the information is too big to fit in the infowindow, i have decided to use a custom overlay. So far, with partially success. Here below is my image. As you can see, the overlay is below the marker. My goal is that the overlay comes above the markers and UI.
Here below is my code sample,
TxtOverlay.prototype.draw = function() {
// create info window
var div = document.createElement("div");
div.id = "mapOverlay";
div.innerHTML = this.txt;
div.style.cssText = 'background-color : #000; color: #FFF; border-radius: 15px; border-style : solid; border-width : 1px;padding: 10px; z-index: 9999; display: block; width: 460px; height: 460px; opacity: 0.4;';
// get projection
var map = this.map;
var overlayProjection = this.getProjection();
var anchor = overlayProjection.fromLatLngToDivPixel(this.pos);
div.style.position = "absolute";
div.style.left = (anchor.x - 240) + 'px';
div.style.top = (anchor.y - 240)+ 'px';
// bind created div to object var
this.div = div;
// add to map
var panes = this.getPanes();
panes.overlayLayer.appendChild(div);
console.log("prototype draw");
}
I have used z-index to 9999 ( there are 4000 markers atm ) but it doesn't work. Maybe it's worth to note that each marker id has its own z-index. (fe marker id = 1 has z-index = 1, marker id = 2 has z-index = 2, marker id = 3 has z-index = 3, ect )
If the overlay is on the top of both markers and UI, then it's easier to catch the click event to remove the overlay instead of manipulating the map and the map-objects.
thank you.
For others whom are also interested to solve this issue, i have found a simple workaround, i am using the floatPane layer, which is above the markers. Then i removed the UI/controls to have a working overlay without map interaction.
In the draw method,
// add to map
var panes = this.getPanes();
panes.floatPane.appendChild(div);
// add event
document.getElementById("mapOverlay").addEventListener('click', this.onRemove, false);
// disable map controls
toggleControls(false);
then when the div got clicked, simply create the next
// remove overlay
TxtOverlay.prototype.onRemove = function(e) {
// remove listener
var element = document.getElementById("mapOverlay");
element.removeEventListener('click', this.onRemove, false);
// and div
var parent = element.parentNode;
parent.removeChild(element);
this._div = null;
// return controls
toggleControls(true);
}
and the toggle method,
// toggles control ; false = no interaction, true = interaction
var toggleControls = function(bool) {
_map.setOptions({draggable: bool, zoomControl: bool, scrollwheel: bool, disableDoubleClickZoom: !bool, scaleControl : bool, zoomControl : bool, panControl : bool});
}
that's all to place and remove an overlay. :-)
Related
I'm using an overlay on Google Maps to plot an alternate image on the map, and it's working fine. I'm using OverlayView(), and I'm able to create some designs on the Overlay too via the drawing API, which is great.
The issue is I want to add an additional overlay in between the two to create some opacity (a slight screening effect) on the underlying map.
Do I use two different overlay methods on the same map?
Here's where it's at.
I have this.maps set to be the GMaps library, and this.map set to be the map instance itself.
createOverlay() {
MyOverlay.prototype = new this.maps.OverlayView()
/* Constructor for the OverlayView() class */
function MyOverlay(bounds, image, map) {
/* Set up local properties */
this.bounds_ = bounds
this.image_ = image
this.map_ = map
/* Set up property to hold the overlay, which is added in onAdd() */
this.div_ = null
/* Explicitly attach this overlay. */
this.setMap(map)
}
/**
* onAdd() - called when the map's panes are ready and the overlay is added
*/
MyOverlay.prototype.onAdd = function () {
/* Create the element to hold the overlay */
const evDiv = document.createElement('div')
evDiv.style.borderStyle = 'none'
evDiv.style.borderWidth = '0px'
evDiv.style.position = 'absolute'
/* Create the image element for the overlay and attach it */
const evImg = document.createElement('img')
evImg.src = this.image_
evImg.style.width = '100%'
evImg.style.height = '100%'
evImg.style.position = 'absolute'
evDiv.appendChild(evImg)
this.div_ = evDiv
/* Attach the elements to the map */
const panes = this.getPanes()
panes.overlayLayer.appendChild(evDiv)
}
/**
* draw() - called whenever the map's tiles are touched to adjust the overlay
* - uses sw and ne coords of the overlay to set its size and peg it to
* the correct position and size.
*/
MyOverlay.prototype.draw = function () {
/* Retrieve the projection from the overlay */
const overlayProjection = this.getProjection()
/* Convert the coords to pixels and resize the div */
const sw = overlayProjection.fromLatLngToDivPixel(this.bounds_.getSouthWest())
const ne = overlayProjection.fromLatLngToDivPixel(this.bounds_.getNorthEast())
const div = this.div_
div.style.left = sw.x + 'px'
div.style.top = ne.y + 'px'
div.style.width = (ne.x - sw.x) + 'px'
div.style.height = (sw.y - ne.y) + 'px'
}
/* onRemove() - Called if we remove the overlay via setting it to 'null' - Not needed yet */
MyOverlay.prototype.onRemove = function () {
this.div_.parentNode.removeChild(this.div_)
this.div_ = null
}
const { src, latMax, lngMax, latMin, lngMin } = this.state.Imagery
const boundaries = new this.maps.LatLngBounds(
new this.maps.LatLng(latMin, lngMin),
new this.maps.LatLng(latMax, lngMax),
)
this.mapOverlay = new MyOverlay(boundaries, src, this.map)
}
The overlay is working fine and loading. I need to add another overlay in between this overlay and the underlying map.
Use panes to layer overlay elements on the map.
This won't work in every scenario, but if you need some type of extra DOM layer between the map base layer and the overlay of the image, it works well. This solution works perfectly for the situation I was in, where I wanted a screen of opacity on top of the underlying map.
You don't create two entirely separate OverlayViews over the same map.
Instead, to do this, assuming you've followed the Custom Overlays Documentation to set up your primary overlay, you add code to the configuration of your OverlayView to make another layer.
In the constructor, you add a holding div for your additional layer, which we'll call layer:
/* Set up properties to hold the overlay, which is added in onAdd() */
this.layer_ = null
this.div_ = null
Then when initiating the Overlay with the onAdd(), you set up your additional div. The crucial part here is properly using the available panes that google maps exposes as MapPanes for your use:
MyOverlay.prototype.onAdd = function () {
/* Create a layer in between google and overlay tiles */
const layerDiv = document.createElement('div')
layerDiv.style.backgroundColor = 'white'
layerDiv.style.opacity = '0.5'
layerDiv.style.position = 'absolute'
/* Any other properties here for your additional layer element */
this.layer_ = layerDiv
/* Attach the elements to the proper pane layers of the map */
const panes = this.getPanes()
panes.mapPane.appendChild(layerDiv)
panes.overlayLayer.appendChild(div)
You don't have to touch the draw() function, but in the onRemove() you need to remove your additional overlay pane:
MyOverlay.prototype.onRemove = function () {
this.layer_.parentNode.removeChild(this.mask_)
this.layer_ = null
this.div_.parentNode.removeChild(this.div_)
this.div_ = null
}
That's it, you should now have an additional overlay appearing between your primary overlay and the underlying map.
I'm trying to reuse the same instance of google.maps.Map when I navigate to/from a view with a MapComponent. When I leave/destroy the MapCompnent, I stash the google map DOM Element in a DIV.style={display:none}
ngOnDestroy() {
google.maps.event.clearInstanceListeners(this.map);
const parent = this.element.nativeElement;
let stash = document.getElementById('stash-google-maps');
if (!stash) {
stash = this.renderer.createElement('DIV');
stash.id = 'stash-google-maps';
stash.style.display = "none";
// stash.style.opacity = "0";
this.renderer.appendChild(this._document.body, stash);
}
while (parent.childNodes.length > 0) {
stash.appendChild(parent.childNodes[0]);
}
}
when I nav back to the view, I move the google map DOM Element back to the MapComponent html. Everything seems to work fine, EXCEPT the map size is wrong. The map is drawing tiles outside the new containing DIV.
I tried to call google.maps.event.trigger(this.map, 'resize'); but this seems to be deprecated in the current v3.34 API.
what should I do?
It turns out new google.maps.Map( el, options) is adding
position: relative;
overflow: hidden;
to the parent element, el
I am new to here maps and need to show a div on marker hover. I have been able to put markers with icons but now need to show a div with some extra information. Does HERE maps API provide this functionality?
Any doc URL or piece of code will be appreciated.
NOTE: I am using HERE maps JS API for web.
You can create a tooltip effect by adding various event listeners to the map to check if the mouse pointer is over an object.
(function (ctx) {
// ensure CSS is injected
var tooltipStyleNode = ctx.createElement('style'),
css = '#nm_tooltip{' +
' color:white;' +
' background:black;' +
' border: 1px solid grey;' +
' padding-left: 1em; ' +
' padding-right: 1em; ' +
' display: none; ' +
' min-width: 120px; ' +
'}';
tooltipStyleNode.type = 'text/css';
if (tooltipStyleNode.styleSheet) { // IE
tooltipStyleNode.styleSheet.cssText = css;
} else {
tooltipStyleNode.appendChild(ctx.createTextNode(css));
}
if (ctx.body) {
ctx.body.appendChild(tooltipStyleNode);
} else if (ctx.addEventListener) {
ctx.addEventListener('DOMContentLoaded', function () {
ctx.body.appendChild(tooltipStyleNode);
}, false);
} else {
ctx.attachEvent('DOMContentLoaded', function () {
ctx.body.appendChild(tooltipStyleNode);
});
}
})(document);
Object.defineProperty(Tooltip.prototype, 'visible', {
get: function() {
return this._visible;
},
set: function(visible) {
this._visible = visible;
this.tooltip.style.display = visible ? 'block' : 'none';
}
});
function Tooltip(map) {
var that = this;
that.map = map;
that.tooltip = document.createElement('div');
that.tooltip.id = 'nm_tooltip';
that.tooltip.style.position = 'absolute';
obj = null,
showTooltip = function () {
var point = that.map.geoToScreen(obj.getPosition()),
left = point.x - (that.tooltip.offsetWidth / 2),
top = point.y + 1; // Slight offset to avoid flicker.
that.tooltip.style.left = left + 'px';
that.tooltip.style.top = top + 'px';
that.visible = true;
that.tooltip.innerHTML = obj.title;
};
map.getElement().appendChild(that.tooltip);
map.addEventListener('pointermove', function (evt) {
obj = that.map.getObjectAt(evt.currentPointer.viewportX,
evt.currentPointer.viewportY);
if(obj && obj.title){
showTooltip();
} else {
that.visible = false;
}
});
map.addEventListener('tap', function (evt){
that.tooltip.visible = false;
});
map.addEventListener('drag', function (evt){
if (that.visible) {
showTooltip();
}
});
};
This is initialised by passing the map object as shown:
function addTooltipControlToMap(map) {
tooltip = new Tooltip(map);
}
The code as written is looking for a .title attribute to be added to the map objects - this could be updated to use .getData() if preferred. Tooltips can be initialised as shown below, taking either text or html:
function addMarkersWithTooltips(map) {
// Simple Marker with tooltip
var brandenburgerTorMarker = new H.map.Marker(
{lat:52.516237, lng: 13.35}),
fernsehturmMarker = new H.map.Marker(
{lat:52.520816, lng:13.409417});
brandenburgerTorMarker.title = 'Brandenburger Tor';
// Marker with HTML Tooltip
fernsehturmMarker.title ='<div>' +
'<h2>Tooltip with HTML content<\/h2>' +
'<img width=\'120\' height=90 src=' +
'\'http://upload.wikimedia.org/wikipedia/commons/' +
'8/84/Berlin-fernsehturm.JPG\' ' +
'alt=\'\'/><br/><b>Fernsehturm, Berlin<\/b>' +
'<\/div>';
// Add the markers onto the map
map.addObjects([brandenburgerTorMarker, fernsehturmMarker]);
}
I have been able to find proper mouse over events for HERE map's markers which are pointerenter and pointerleave and sample code to use these events is:
// After Initializing map with your own credentials.
var map = new H.Map(document.getElementById('map'),
defaultLayers.normal.map,{
center: {lat: LAT_VAL, lng: LNG_VAL},
zoom: 12
});
var domMarker = new H.map.DomMarker(coords, {
icon: domIcon
});
var bubble;
domMarker.addEventListener('pointerenter', function(evt) {
bubble = new H.ui.InfoBubble({lat:"SOME_VALUE",lng:"SOME_VALUE"}, {
content: "Your content come here"
});
ui.addBubble(bubble);
}, false);
domMarker.addEventListener('pointerleave', function(evt) {
bubble.close();
}, false);
map.addObject(domMarker);
Depending on the api version you are using, you may find what you are looking for inside the documentation pdf (or at least start from there).
Supposing you need do make some HTML styled marker, you may need:
DomMarker (instead of a Marker, because it allows you to use ->2)
DomIcon (which can embed html)
An example can be found here https://developer.here.com/apiexplorer-v2-sample-data/template-web-default/examples/map-with-dom-marker/index.html
Anyway, if you need to show informations about the marker, I would suggest to use InfoBubbles, which have been developed for this purpose.
From the 3.0.5 docs:
// Create an info bubble object at a specific geographic location:
ui = H.ui.UI.createDefault(self.map, defaultLayers);
var bubble = new H.ui.InfoBubble({ lng: 13.4, lat: 52.51 }, {
content: '<b>Hello World!</b>'
});
// Add info bubble to the UI:
ui.addBubble(bubble);
To show them, you should attach an event to the marker tap event:
marker.addEventListener('tap', function (evt) {
//create and add the bubble
}
In any case, you can find the documentation of your api version here: https://developer.here.com/documentation/versions
You do not have "hover" listener for marker,
but you can show infoBubble on click
http://heremaps.github.io/examples/explorer.html#infobubble-on-marker-click
If this doesn't work for you, you will have to use jquery and to bind "hover" on HTML marker element. (This is not very easy task)
I have been able to integrate markers to the mapbox we are using, but still wonder if we can get a click on them. If so how?
Following is my code:
<style>
/*
* Unlike other icons, you can style `L.divIcon` with CSS.
* These styles make each marker a circle with a border and centered text.
*/
.count-icon1 {
background:url(images/redpin.png);
color:#000;
font-weight:600;
text-align:center;
padding:19px 0 0 0px; font-size:180%;
}
.count-icon2 {
background:url(images/greenpin.png);
color:#000;
font-weight:600;
text-align:center;
padding:19px 0 0 0px; font-size:180%;
}
</style>
js code:
var defaultLat = 39.12367;
var defaultLon = -76.81229;
if($scope.currentLocDetails != null){
if($scope.currentLocDetails.Lat != null && $scope.currentLocDetails.Lon != null){
defaultLat = $scope.currentLocDetails.Lat;
defaultLon = $scope.currentLocDetails.Lon;
}
}
var x = 0;
if(map != null)
map.remove();
map = L.mapbox.map('map_view', 'your key here').setView([defaultLat, defaultLon], 9);
for (var i = 0; i < responseData.JobLocation.length; i++) {
var eachObj = responseData.JobLocation[i];
if(eachObj.Lat != null && eachObj.Lon != null){
x++;
// Use a little math to position markers.
// Replace this with your own code.
L.marker([
eachObj.Lat,
eachObj.Lon
], {
icon: L.divIcon({
// Specify a class name we can refer to in CSS.
className: ((currentSelectedIndex + 1) == i + 1)?'count-icon1':'count-icon2',
// Define what HTML goes in each marker.
html: i + 1,
// Set a markers width and height.
iconSize: [65, 94]
})
}).addTo(map);
}
}
I tried doing a bit R & D, but get to no where:
We need to use featureLayer, but dunno how.
For the click feature we need to follow this code, but how?
// Listen for individual marker clicks.
myLayer.on('click',function(e) {
// Force the popup closed.
e.layer.closePopup();
var feature = e.layer.feature;
var content = '<div><strong>' + feature.properties.title + '</strong>' +
'<p>' + feature.properties.description + '</p></div>';
info.innerHTML = content;
});
Any help with this is really appreciated.
Thanks
I believe there are various ways of doing this with Mapbox, unfortunately I don't have access to my project where I use it right now so I'm just going off the Mapbox documentation.
Following your example this looks the simplest - if you have added marker, for example:
var marker = L.marker([43.6475, -79.3838], {
icon: L.mapbox.marker.icon({
'marker-color': '#9c89cc'
})
})
.bindPopup('<p>Your html code here</p>')
.addTo(map);
You can pass whatever HTML you want in the bindPopup argument.
https://www.mapbox.com/mapbox.js/example/v1.0.0/clicks-in-popups/
Then it should be be pretty simple via 'addEventListener('click', function)' on that marker variable.
Or alternatively -
myLayer.on('click', function(e) {
resetColors();
e.layer.feature.properties['old-color'] = e.layer.feature.properties['marker-color'];
e.layer.feature.properties['marker-color'] = '#ff8888';
myLayer.setGeoJSON(geoJson);
});
map.on('click', resetColors);
Effectively add an event listener on the map variable - and then listen to what you've clicked on via the event argument passed to the event listener.
This may be useful: https://www.mapbox.com/mapbox.js/example/v1.0.0/change-marker-color-click/
Good luck!
I am looking for a Google Map V3 context menu library. I have found some code examples here
Gizzmo's blog
Google API tips
GMap3
How I got ..
Stack overflow question Google maps v3 - Contextual menu available? of April also just came up with the above examples. So did Gmap3 adding a simple context menu .
But maybe somebody has encapsulated the examples in a reusable library or found something in the meantime. Obviously there was something for V2.
-- Updated 2012-05-31 --
I have found another one http://googlemapsmania.blogspot.de/2012/04/create-google-maps-context-menu.html , but did not have the time to test it yet.
I don't think you need a library for this. I'd start by trying:
var contextMenu = google.maps.event.addListener(
map,
"rightclick",
function( event ) {
// use JS Dom methods to create the menu
// use event.pixel.x and event.pixel.y
// to position menu at mouse position
console.log( event );
}
);
This assumes your map was created with:
var map = new google.maps.map( { [map options] } );
The event object inside the callback has 4 properties
latLng
ma
pixel
where pixel.x and pixel.y are the offset where your click event triggered - counted from the upper left corner of the canvas holding the map object.
I have created a working JS Fiddle for showing context menu as well as the ability to have clickable items on this context menu.
It shows a clickable Context Menu when a marker is right clicked on Google map.
Basically it makes use of an OverlayView on map. BTW its just a demo.
var loc, map, marker, contextMenu;
ContextMenu.prototype = new google.maps.OverlayView();
/**
* onAdd is called when the map's panes are ready and the overlay has been
* added to the map.
*/
ContextMenu.prototype.onAdd = function() {
$("<div id='cMenu' class='context-menu-marker'></div>").appendTo(document.body);
var divOuter = $("#cMenu").get(0);
for(var i=0;i < this.menuItems.length;i++) {
var mItem = this.menuItems[i];
$('<div id="' + mItem.id + '" class="options-marker">' +
mItem.label + '</div>').appendTo(divOuter);
}
this.div_ = divOuter;
// Add the element to the "overlayLayer" pane.
var panes = this.getPanes();
//panes.overlayLayer.appendChild();
panes.overlayMouseTarget.appendChild(this.div_);
var me = this;
for(var i=0;i < this.menuItems.length;i++) {
var mItem = this.menuItems[i];
var func = function() {
me.clickedItem = this.id;
google.maps.event.trigger(me, 'click');
};
google.maps.event.addDomListener($("#" + mItem.id).get(0), 'click', $.proxy(func, mItem));
}
google.maps.event.addListener(me, 'click', function() {
alert(me.clickedItem);
});
};
ContextMenu.prototype.draw = function() {
var div = this.div_;
div.style.left = '0px';
div.style.top = '0px';
div.style.width = '100px';
div.style.height = '50px';
};
// The onRemove() method will be called automatically from the API if
// we ever set the overlay's map property to 'null'.
ContextMenu.prototype.onRemove = function() {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
};
// Set the visibility to 'hidden' or 'visible'.
ContextMenu.prototype.hide = function() {
if (this.div_) {
// The visibility property must be a string enclosed in quotes.
this.div_.style.visibility = 'hidden';
}
};
ContextMenu.prototype.show = function(cpx) {
if (this.div_) {
var div = this.div_;
div.style.left = cpx.x + 'px';
div.style.top = cpx.y + 'px';
this.div_.style.visibility = 'visible';
}
};
function ContextMenu(map,options) {
options = options || {}; //in case no options are passed to the constructor
this.setMap(map); //tells the overlay which map it needs to draw on
this.mapDiv = map.getDiv(); //Div container that the map exists in
this.menuItems = options.menuItems || {}; //specific to context menus
this.isVisible = false; //used to hide or show the context menu
}
function initialize() {
loc = new google.maps.LatLng(62.323907, -150.109291);
var options = {};
var menuItems=[];
menuItems.push({id:"zoomIn", className:'context_menu_item', eventName:'zoom_in_click', label:'Zoom in'});
menuItems.push({id:"zoomOut", className:'context_menu_item', eventName:'zoom_out_click', label:'Zoom out'});
options.menuItems = menuItems;
//=========================================
map = new google.maps.Map(document.getElementById("map"), {
zoom: 12,
center: loc,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
marker = new google.maps.Marker({
map: map,
position: loc,
visible: true
});
contextMenu = new ContextMenu(map, options);
google.maps.event.addListener(marker, 'rightclick', function(mouseEvent){
contextMenu.hide();
this.clickedMarker_ = this;
var overlayProjection = contextMenu.getProjection();
var cpx = overlayProjection.fromLatLngToContainerPixel(mouseEvent.latLng);
contextMenu.show(cpx);
map.setOptions({ draggableCursor: 'pointer' });
});
// Hide context menu on several events
google.maps.event.addListener(map,'click', function(){
map.setOptions({ draggableCursor: 'grab' });
contextMenu.hide();
});
}
google.maps.event.addDomListener(window, 'load', initialize);
Fiddle link:
http://jsfiddle.net/jEhJ3/3409/
You can add context menu very easily in google map by following these steps:
Add a custom control of google maps, hide that control on page load.
Add a right click event handler on map.
Show that custom control on right click at correct position using pixel property of right click event parameter.
Moreover, Following is working snippet, open it in full page (use you own key to avoid that google billing error):
var map;
var karachi = {
lat: 24.8567575,
lng: 66.9701725
};
$(document).ready(function() {
initMap();
});
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
zoom: 13.5,
center: karachi
});
let contextMenu = document.getElementById('contextMenu');
map.controls[google.maps.ControlPosition.TOP_CENTER].push(contextMenu);
hideContextMenu();
google.maps.event.addListener(map, "rightclick", function(event) {
showContextMenu(event);
});
google.maps.event.addListener(map, "click", function(event) {
hideContextMenu();
});
}
function showContextMenu(event) {
$('#contextMenu').css("display", "block");
$('#contextMenu').css({
left: event.pixel.x,
top: event.pixel.y
})
}
function hideContextMenu() {
$('#contextMenu').css("display", "none");
}
#map {
height: 100%;
}
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
.contextMenu {
background-color: rgb(255, 255, 255);
border: 2px solid rgb(255, 255, 255);
border-radius: 3px;
box-shadow: rgba(0, 0, 0, 0.3) 0px 2px 6px;
cursor: pointer;
font-size: 1rem;
text-align: center;
color: #0d1f49;
width: 20vw;
margin: 1px;/*Please note that this margin is necessary otherwise browser will open its own context menu*/
}
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAGlM3LLIL2j4Wm-WQ9qUz7I7ZpBsUx1X8">
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="map"></div>
<div id="contextMenu" class="contextMenu">
<div onclick="alert('On click of item 1 is called')">
Item 1
</div>
</div>
Go to this demo-purpose website: http://easysublease.org/mapcoverjs/
For context menu, I do not suggest implementing one subclass of the overlayView Class provided by Google Maps API. First, one instance of subclass of overlayView should be added to the five panes provided by Google. More possibly one should add this instance to pane overlayMouseTarget .
But, this instance is "shadowed" by other dom over it. So normal original browser event such mouseover, mouseout cannot reach this instance.
One must use Google Maps API method: addDomListener to handle it(why?). It requires lots of JavaScript code to implement different event handlers, do lots of css class adding and deleting just to realize some visual effects, which could be done using several lines of CSS code if this instance is outside the map container.
So actually converting one external dom outside google map container into one context menu has merit that it can receive original DOM events from browser. Also using some external library can make the target behave better. As context menu, it should not only be able to handle original events, but also those events from Map.
-----------see implementations below------------------------
At the map part HTML, this is the code:
<div id="mapcover">
<div id="mapcover-map"></div> <!-- this is map container-->
<div id="demoControlPanel" class="mc-static2mapcontainer panel">I am map UI control button's container, I think one can use jQuery UI to make me look better<br><br>
<div id="zoom-in-control" class="text-center">zoomIn</div>
<div id="zoom-out-control" class="text-center">zoomOut</div>
</div>
<div id="demoContextPanel" class="mc-ascontextmenu panel">
I am map context menu container, you can sytle me and add logic to me, just as normal DOM nodes.
since I am not in Map Container at all!
<div class="text-center">
<div role="group" aria-label="..." class="btn-group">
<button id="place-marker1" type="button" class="btn btn-default">Marker1</button>
<button id="place-marker2" type="button" class="btn btn-default">Marker2</button>
</div>
</div>
<div class="form-group">
<label for="content-marker1">Content of next Marker1</label>
<input id="content-marker1" type="text" placeholder="New of Marker1!" class="form-control">
</div>
</div>
</div>
It shows how one developer can convert one external DOM (id=demoContextPanel) into one map context menu by just adding one css class ".mc-ascontextmenu"!
That pages uses mapcover.js, which helps developer to manage some key components of Map such as Map control UIs, context menu, and customized markers. Then Developers have full freedom to style its map UIs.
If you need more, you can go to its Github see readme.md: https://github.com/bovetliu/mapcover