I want to run code in a Template.foo.rendered only once. I have a Leaflet map and a locate-control plugin integrated. It works quite good so far but it seems the rendered template is rerun frequently. So my marker for locating the user on the map is reset every few seconds. I just want to initialize it once.
var map, mapInit;
map = void 0;
mapInit = function (element){
map = L.mapbox.map(element, 'examples.map-20v6611k').setView(new L.LatLng(52.02312,13.02627), 11);
L.control.locate({drawCircle: true, position: 'topright'}).addTo(map);
}
Template.map.rendered = function () {
$(window).resize(function () {
var h = $(window).height();
$mc = $('#map');
$mc.css('height', h);
}).resize();
if (!this.rendered) {
mapInit('map');
this.rendered = true;
}
}
You may want to save the map's DOM elements to a global or app variable (here, Map), and reinsert them in the DOM later:
var Map;
...
var mapDomElement = $('#map');
if (Map.domElements) {
mapDomElement.replaceWith(Map.domElements);
} else {
Map.mapObject = L.mapbox.map(...) // instantiate map here
Map.domElements = mapDomElement;
}
Note re. the height recomputation: you don't need to do that. Use the CSS flex box model to maximize map height:
<div style="display: flex; flex-flow: column; min-height: 100%">
<div id="map" style="flex: 1"></div>
<div>
... map controls here
</div>
</div>
See this answer on the flex model. The "stuff goes here" div is your map.
Related
Relatively new JavaScript user here, first question.
So I have a choropleth leaflet map that uses a jQuery slider (via https://github.com/dwilhelm89/LeafletSlider) to shift between years. The map contains about 50 years of global data, with each overlay layer corresponding to a layergroup containing each country's data for the appropriate year.
The purpose of the slider is to allow the user to quickly shift between years. However, I would like a visual cue to let the user know what year is being displayed at any moment. Is it possible to display something like a text box on the map that displays the name of the current overlay layer and automatically updates whenever the overlay layer switches? (the name of each layergroup is its respective year)
I know the textbox part is certainly possible
(Overlaying a text box on a leaflet.js map), but I'm not sure how to dynamically update it with the necessary info.
Thanks! Let me know if you need my code and I'll post it.
Okay, I thought a bit and here's a quick solution.
var sliderControl = null;
var map = L.map("map").setView([51.95, 7.6], 9);
L.tileLayer("//{s}.tile.osm.org/{z}/{x}/{y}.png", {
attribution:
'© OpenStreetMap contributors',
}).addTo(map);
//Fetch some data from a GeoJSON file
$.getJSON(
"https://dwilhelm89.github.io/LeafletSlider/points.json",
function (json) {
var testlayer = L.geoJson(json);
var sliderControl = L.control.sliderControl({
position: "topright",
layer: testlayer,
range: true,
});
//Make sure to add the slider to the map ;-)
map.addControl(sliderControl);
//An initialize the slider
sliderControl.startSlider();
}
);
map.on("layeradd", function () {
map.eachLayer(function (layer) {
if (layer instanceof L.Marker) {
let desc = document.querySelector(".description");
// desc.textContent = JSON.stringify(layer.getLatLng());
desc.textContent = layer.feature.properties.time;
}
});
});
// create legend
const legend = L.control({ position: "bottomleft" });
legend.onAdd = function () {
let div = L.DomUtil.create("div", "description");
div.className = "description";
return div;
};
legend.addTo(map);
*,
:after,
:before {
box-sizing: border-box;
padding: 0;
margin: 0;
}
html {
height: 100%;
}
body,
html,
#map {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.description {
border: 1px solid black;
background: #fff;
}
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.6.0/dist/leaflet.css" />
<link rel="stylesheet" href="https://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" type="text/css">
<script src="https://unpkg.com/leaflet#1.6.0/dist/leaflet.js"></script>
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="https://code.jquery.com/ui/1.9.2/jquery-ui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.2/jquery.ui.touch-punch.min.js"></script>
<script src="https://rawgit.com/dwilhelm89/LeafletSlider/master/SliderControl.js" type="text/javascript"></script>
<div id="map"></div>
And I recommend using the newer version of this plugin ;)
There you have the method event
And the easier way to download the data about the marker.
I've used Leaflet, Mapbox, and Google Maps on some personal and commercial projects, and whenever I wanted to overlay some information, I'd just use simple HTML elements. All you need to do is render whatever elements you want on the screen the same way you would normally, just ensure that you use correct position and applicable positioning units and ensure you have a higher z-index on your element that you want to show, i.e. your year indicator, than you do on your map element. Treat it just like you would any other HTML!
Edit:
Here is an example screenshot: https://imgur.com/a/2fXf5CI. Also, if you aren't already using a position property on your Leaflet map, you should go ahead and add a position: relative; property to the selector for the map so that you can also assign it a z-index. And then, in your year indicator's styles, give it a higher z-index value than the one you gave to your Leaflet map.
I am working on a canvas library and I have the following class:
export default class Layer {
constructor(ypcanvas) {
this.ypcanvas = ypcanvas;
this.container = ypcanvas.container;
this.can = document.createElement("canvas");
this.ctx = this.can.getContext("2d");
}}
(This is just a little excerpt of the Layer class)
You can import the class and then create a new Layer like this:
let layer = new Layer(ypcanvas);
How could i accomplish an event like the following:
layer.on('mouseout', function () { });
or
layer.mousedown(function () { })
Or somethign equivalent to that, so that the user of my library can just call that event without having to addEventListener the layer canvas.
Thx in advance.
You can do something like this:
class MyLibClass {
constructor(elementId) {
this.elementId = elementId
}
mouseDown(callback) {
document.getElementById(this.elementId).addEventListener("mousedown", callback)
}
}
new MyLibClass("lib").mouseDown(() => alert("hey"))
#lib {
width: 100px;
height: 100px;
background: blue;
}
<div id="lib"></div>
Assuming you're going to add the canvas somehow to the document tree – otherwise you should describe more in details your system –, you can just proxy the lister to the canvas:
export default class Layer {
constructor(ypcanvas) {
this.ypcanvas = ypcanvas;
this.container = ypcanvas.container;
this.can = document.createElement("canvas");
this.ctx = this.can.getContext("2d");
}
on(type, lister) {
this.can.addEventListener(type, lister);
}
off(type,lister) {
this.can.removeEventListener(type, lister);
}
}
If instead it's just a memory canvas, then you have to implement a coordinates system and z-index to emit the right event from the physical canvas based on the layer copied – and that is indeed more complicated.
However, it's still the same principle: if you don't want that the final user adds the events to the canvas, you have to do it yourself at a certain point.
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 have a simple linear chart built with Chart.js library.
And i want to allow user to drag points on chart for dynamically change data of it. I tied chartjs-plugin-draggable but it works for me only with annotations. I need graph exactly like this:
https://www.rgraph.net/canvas/docs/adjusting-line.html
But use new graph library in project is not good solution :(
Also i tried to play with dot event's.
UPDATE:
With angular i created something like this.
Maybe if there is no way to add drag&drop to points, there will be a hack to put "sliders" with absolute position on graph on points positions. I didn't find any info too :(
In case anyone is looking for a solution that doesn't require the use of plugins, it's pretty straightforward to do it in vanilla chart.js.
Here's a simple working example - just click and drag a data point
// some data to be plotted
var x_data = [1500,1600,1700,1750,1800,1850,1900,1950,1999,2050];
var y_data_1 = [86,114,106,106,107,111,133,221,783,2478];
var y_data_2 = [2000,700,200,100,100,100,100,50,25,0];
// globals
var activePoint = null;
var canvas = null;
// draw a line chart on the canvas context
window.onload = function () {
// Draw a line chart with two data sets
var ctx = document.getElementById("canvas").getContext("2d");
canvas = document.getElementById("canvas");
window.myChart = Chart.Line(ctx, {
data: {
labels: x_data,
datasets: [
{
data: y_data_1,
label: "Data 1",
borderColor: "#3e95cd",
fill: false
},
{
data: y_data_2,
label: "Data 2",
borderColor: "#cd953e",
fill: false
}
]
},
options: {
animation: {
duration: 0
},
tooltips: {
mode: 'nearest'
}
}
});
// set pointer event handlers for canvas element
canvas.onpointerdown = down_handler;
canvas.onpointerup = up_handler;
canvas.onpointermove = null;
};
function down_handler(event) {
// check for data point near event location
const points = window.myChart.getElementAtEvent(event, {intersect: false});
if (points.length > 0) {
// grab nearest point, start dragging
activePoint = points[0];
canvas.onpointermove = move_handler;
};
};
function up_handler(event) {
// release grabbed point, stop dragging
activePoint = null;
canvas.onpointermove = null;
};
function move_handler(event)
{
// locate grabbed point in chart data
if (activePoint != null) {
var data = activePoint._chart.data;
var datasetIndex = activePoint._datasetIndex;
// read mouse position
const helpers = Chart.helpers;
var position = helpers.getRelativePosition(event, myChart);
// convert mouse position to chart y axis value
var chartArea = window.myChart.chartArea;
var yAxis = window.myChart.scales["y-axis-0"];
var yValue = map(position.y, chartArea.bottom, chartArea.top, yAxis.min, yAxis.max);
// update y value of active data point
data.datasets[datasetIndex].data[activePoint._index] = yValue;
window.myChart.update();
};
};
// map value to other coordinate system
function map(value, start1, stop1, start2, stop2) {
return start2 + (stop2 - start2) * ((value - start1) / (stop1 - start1))
};
body {
font-family: Helvetica Neue, Arial, sans-serif;
text-align: center;
}
.wrapper {
max-width: 800px;
margin: 50px auto;
}
h1 {
font-weight: 200;
font-size: 3em;
margin: 0 0 0.1em 0;
}
h2 {
font-weight: 200;
font-size: 0.9em;
margin: 0 0 50px;
color: #555;
}
a {
margin-top: 50px;
display: block;
color: #3e95cd;
}
<!DOCTYPE html>
<html>
<!-- HEAD element: load the stylesheet and the chart.js library -->
<head>
<title>Draggable Points</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.9.3/dist/Chart.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<!-- BODY element: create a canvas and render a chart on it -->
<body>
<!-- canvas element in a container -->
<div class="wrapper">
<canvas id="canvas" width="1600" height="900"></canvas>
</div>
<!-- call external script to create and render a chart on the canvas -->
<script src="script.js"></script>
</body>
</html>
Update: My previous answer got deleted because it only featured a link to a plugin solving the issue, however here comes the explanation to what it does:
The general procedure on how to achieve the desired behaviour is to
Intercept a mousedown (and check if it's a dragging gesture) on a given chart
Check if the mousedown was over a data point using the getElementAtEvent function
On mousemove, translate the new Y-Pixel value into a data coordinate using the axis.getValueForPixel function
Synchronously update the chart data using chart.update(0)
as pointed out in this Chart.js issue.
In order to intercept the mousedown, mousemove and mouseup events (the dragging gesture), event listeners for said events need to be created. In order to simplify the creation of the listeners one may use the d3 library in this case as follows:
d3.select(chartInstance.chart.canvas).call(
d3.drag().container(chartInstance.chart.canvas)
.on('start', getElement)
.on('drag', updateData)
.on('end', callback)
);
On mousedown (the 'start' event here), a function (getElement) may be called thatfetches the closest chart element to the pointers location and gets the ID of the Y-Scale
function getElement () {
var e = d3.event.sourceEvent
element = chartInstance.getElementAtEvent(e)[0]
scale = element['_yScale'].id
}
On mousemove ('drag'), the chart data is supposed to be updated according to the current Y-Pixel value of the pointer. We can therefore create an updateData function that gets the position of the clicked data point in the charts data array and the according dataset like this
function updateData () {
var e = d3.event.sourceEvent
var datasetIndex = element['_datasetIndex']
var index = element['_index']
var value = chartInstance.scales[scale].getValueForPixel(e.clientY)
chartInstance.data.datasets[datasetIndex].data[index] = value
chartInstance.update(0)
}
And that's it! If you need to store the resulting value after dragging, you may also specify a callback function like this
function callback () {
var datasetIndex = element['_datasetIndex']
var index = element['_index']
var value = chartInstance.data.datasets[datasetIndex].data[index]
// e.g. store value in database
}
Here is a working fiddle of the above code. The functionality is also the core of the Chart.js Plugin dragData, which may be easier to implement in many cases.
Here is how I fixed using both touchscreen or mouse event x,y coordinates for the excellent d3 example above by wrapping event screen coordinates in a more "generic" x,y object.
(Probably d3 has something similar to handle both types of events but lot of reading to find out..)
//Get an class of {points: [{x, y},], type: event.type} clicked or touched
function getEventPoints(event)
{
var retval = {point: [], type: event.type};
//Get x,y of mouse point or touch event
if (event.type.startsWith("touch")) {
//Return x,y of one or more touches
//Note 'changedTouches' has missing iterators and can not be iterated with forEach
for (var i = 0; i < event.changedTouches.length; i++) {
var touch = event.changedTouches.item(i);
retval.point.push({ x: touch.clientX, y: touch.clientY })
}
}
else if (event.type.startsWith("mouse")) {
//Return x,y of mouse event
retval.point.push({ x: event.layerX, y: event.layerY })
}
return retval;
}
.. and here is how I would use it in the above d3 example to store the initial grab point Y. And works for both mouse and touch.
Check the Fiddle
Here how I solved the problem with using d3 and wanting to drag the document on mobile or touch screens. Somehow with the d3 event subscription all Chart area events where already blocked from bubbling up the DOM.
Was not able to figure out if d3 could be configured to pass canvas events on without touching them. So in a protest I just eliminated d3 as it was not much involved other than subscribing events.
Not being a Javascript master this is some fun code that subscribes the events the old way. To prevent chart touches from dragging the screen only when a chart point is grabed each of the handlers just have to return true and the event.preventDefault() is called to keep the event to your self.
//ChartJs event handler attaching events to chart canvas
const chartEventHandler = {
//Call init with a ChartJs Chart instance to apply mouse and touch events to its canvas.
init(chartInstance) {
//Event handler for event types subscribed
var evtHandler =
function myeventHandler(evt) {
var cancel = false;
switch (evt.type) {
case "mousedown":
case "touchstart":
cancel = beginDrag(evt);
break;
case "mousemove":
case "touchmove":
cancel = duringDrag(evt);
break;
case "mouseup":
case "touchend":
cancel = endDrag(evt);
break;
default:
//handleDefault(evt);
}
if (cancel) {
//Prevent the event e from bubbling up the DOM
if (evt.cancelable) {
if (evt.preventDefault) {
evt.preventDefault();
}
if (evt.cancelBubble != null) {
evt.cancelBubble = true;
}
}
}
};
//Events to subscribe to
var events = ['mousedown', 'touchstart', 'mousemove', 'touchmove', 'mouseup', 'touchend'];
//Subscribe events
events.forEach(function (evtName) {
chartInstance.canvas.addEventListener(evtName, evtHandler);
});
}
};
The handler above is initiated like this with an existing Chart.js object:
chartEventHandler.init(chartAcTune);
The beginDrag(evt), duringDrag(evt) and endDrag(evt) have the same basic function as in the d3 example above. Just returns true when wanting to consume the event and not pasing it on for document panning and similar.
Try it in this Fiddle using a touch screen. Unless you touch close to select a chart point the rest of the chart will be transparent to touch/mouse events and allow panning the page.
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