I have a GMap inside a div with display:none;.
Inside the div is a PrimeFaces map component.
After clicking on a button, the content of the div element should appear, but only a blank page is showing.
<div class="form-group" id="mapContainer" style="display:none;">
<p:gmap id="gmap" center="51.30993291552862,9.448113441467285" zoom="15" type="terrain" style="width:100%;height:700px;" widgetVar="gmap" navigationControl="false" />
</div>
But outside the div element, the map is built and showing correctly.
How can I solve this problem?
As mentioned in one of comments, google map object is not initialized if mapContainer div is hidden (display: none) during page load...
so you will need to "manually" initialize google map object after you make mapContainer div visible.
Here is fully working code (based on your posted code) that will do what you need:
Add this JavaScript to your page
<script>
function resizeElement(elementId,width,height){
console.log("Resizing element " + elementId + " W/H="+ width + "/" + height);
var element = document.getElementById(elementId);
element.style.width=width+"px";
element.style.height=height+"px"
}
function resizePfGmapInsideWrapperElement(wrapperElementId){
var wrapperElement=document.getElementById(wrapperElementId);
var width=wrapperElement.clientWidth-40;
var height=wrapperElement.clientHeight-60;
resizeElement("gmap",width,height);
}
function resizePfGmapInsideDiv(){
var gmap = PF('gmap').getMap();
console.log(gmap);
resizePfGmapInsideWrapperElement("mapContainer");
}
function toggleDivVisibility() {
var div = document.getElementById("mapContainer");
if(div.style.display === "block"){
div.style.display = "none";
}else{
div.style.display = "block";
div.style.width="600px";
div.style.height="400px";
initializeGmap();
resizePfGmapInsideDiv();
}
}
function initializeGmap() {
var myOptions = {
zoom: 15,
center: new google.maps.LatLng(51.30993291552862, 9.448113441467285),
mapTypeId: google.maps.MapTypeId.TERRAIN
}
new google.maps.Map(document.getElementById("gmap"),myOptions);
}
</script>
and, just for testing purposes, add a button that will toggle mapContainer div visibility
<p:commandButton value="Show/hide map" onclick="toggleDivVisibility();"/>
The crucial JS method is self-explanatory initializeGmap() executed in the moment when you make div visible: it will create "a new map inside of the given HTML container, which is typically a DIV element." as stated in documentation referenced above.
Related
I have a form and inside the form is a leaflet map. I want to move between elements pressing tab key and I do not want the map or his elements ( buttons ,markers,etc) to get focus. How can I add tabindex="-1" to maps controls and elements to prevent focus, or what I can do to prevent focus?
Here is a jsfiddle to show my scenario: http://jsfiddle.net/kedar2a/LnzN2/2/
var osmUrl = 'http://{s}.tile.osm.org/{z}/{x}/{y}.png', osmAttrib = '© <a ref="http://openstreetmap.org/copyright">OpenStreetMap</a> contributors',
osm = L.tileLayer(osmUrl, {attribution: osmAttrib });
var map = L.map('map').setView([19.04469, 72.9258], 12).addLayer(osm);
var marker = L.marker([19.04469, 72.9258]).addTo(map);
#map {
height: 150px;
width: 300px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.5.1/leaflet.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.5.1/leaflet.js"></script>
<input type="text" autofocus />
<div id="map"></div>
<input type="text" />
There is no one-shot solution but you can achieve this by disabling each map element in three steps:
Disable focus for markers:
Disable keyboard support for markers by adding keyboard:false to the marker element.
Add tabIndex="-1" attribute to all <a> elements located inside .leaflet-control divs
var elements = document.querySelectorAll(".leaflet-control a");
for (var i = 0; i < elements.length; ++i) {
elements[i].setAttribute("tabindex", "-1");
}
Disable focus for Close Button inside any open marker popup.
var marker = L.marker(e.latlng, {
draggable: true,
keyboard: false,
title: "Resource location",
alt: "Resource Location",
riseOnHover: true
}).addTo(map)
.bindPopup(e.latlng.toString()).on('popupopen',
function(popup) {
//disable focus of close button
var elCloseButton = document.getElementsByClassName("leaflet-popup-close-button")[0];
elCloseButton.setAttribute("tabindex", "-1");
}).openPopup();
See my implementation: http://jsfiddle.net/trkaplan/bv763tkf/
I'm trying to include a google places autocomplete input box in my React app.
I've followed the guide here to place an <input> text field, and initializing the search box like so:
export default class MySearch extends class Component {
...
componentDidMount() {
var defaultBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(-33.8902, 151.1759),
new google.maps.LatLng(-33.8474, 151.2631));
var input = document.getElementById('searchTextField');
var searchBox = new google.maps.places.SearchBox(input, {
bounds: defaultBounds
});
}
render() {
return (
...
<input id="searchTextField"
type="text"
className="form-control"
placeholder="Search for a location"
/>
);
}
}
But I don't see any suggestions dropping down from the text field.
I inspected the networks tab, to see whether API requests are being hit as I type, and I, not only see requests, but responses from the API, with matching locations, based on my search term, as I type through.
I have no idea why the received suggestions are not being displayed in a dropdown suggestions list below my input box.
Thanks in advance :)
Update
PS: I've placed the text box inside a bootstrap modal. When I place exactly the same text box, outside the bootstrap modal, it works like a breeze.
Any idea why the text box isn't showing suggestions while inside the modal?
It is a styling issue, as the modal's z-index > dropdown's (.pac-container's) z-index. Fixed it with the following CSS snippet:
.pac-container {
background-color: #FFF;
z-index: 2001;
position: fixed;
display: inline-block;
float: left;
}
.modal{
z-index: 2000;
}
.modal-backdrop{
z-index: 1000;
}
DOM reference (findDOMNode)
You should not select a dom element with id in react component. Use ref (reference) instead. Learn more about findDOMNode here https://facebook.github.io/react/docs/react-dom.html#finddomnode
import { findDOMNode } from 'react-dom';
export default class MySearch extends class Component {
componentDidMount() {
var defaultBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(-33.8902, 151.1759),
new google.maps.LatLng(-33.8474, 151.2631));
var input = findDOMNode(this.refs['searchTextField']);
var searchBox = new google.maps.places.SearchBox(input, {
bounds: defaultBounds
});
}
render() {
return (
<input ref="searchTextField"
type="text"
className="form-control"
placeholder="Search for a location"
/>
);
}
}
I tried the solution here Google maps in hidden div but it didn't work. Think it might be an issue with react. The map loads fine when not placed in the hidden div.
When state.hideScore turns false in the parent container, the map shows up but as a gray box. any help?
Parent container
<div hidden={this.state.hideScore}>
<ScoreExplanation score={this.state.score} />
<br />
<ResultList data={this.state.scoreArray} />
<ResultMap />
</div>
Component
import React, { Component, PropTypes } from 'react';
var $ = require ('jquery');
var map;
var ResultMap = React.createClass({
componentDidMount: function() {
// ** Instantiating the Map ** //
map = new google.maps.Map(document.getElementById('map'), {
center: new google.maps.LatLng(-34.397, 150.644),
zoom: 14
});
google.maps.event.trigger(map, 'resize');
map.setZoom( map.getZoom() );
},
render: function() {
return (
<div style={{overflow:'visible',height:'300px',width:'300px'}} id="map"></div>
);
}
});
export default ResultMap;
Instead of initializing the map in componentDidMount, you should instead initialize it when the parent re-renders after calling setState to change this.state.hideScore. What's happening right now is, your map is getting loaded into the ResultMap component before its parent is visible. You should instead wait until the parent component is visible, then instantiate the ResultMap.
Example:
Parent component render method
// I prefer using CSS classes to hide/display components.
// Initialize hideScore to 'hidden' and see CSS.
render () {
return (
<div className={this.state.hideScore}>
<ScoreExplanation score={this.state.score} />
<br />
<ResultList data={this.state.scoreArray} />
<div id='result-container'></div>
</div>
)
}
Parent component click handler method (Can be whatever method).
handleClick = () => {
this.setState({
hideScore: 'shown' // See CSS.
});
ReactDOM.render(
<ResultMap />,
document.getElementById('result-container')
);
}
CSS
.shown {
display: block; // Or whatever is your preference.
}
.hidden {
display: none;
}
I am using the Google Maps V3 Javascript API and would like to style the zoom control. Though the Google Documentation has information about moving the Custom Controls it doesn't seem to have any for styling the custom controls. https://developers.google.com/maps/documentation/javascript/controls#ControlModification
What I would like to do is be able to style the existing zoom control.
Is there a way to do style the EXISTING control? I would prefer to do this over creating a new custom control.
there's a option for that using google's map API and Custom Control setup, here's a fragment of the code I found:
var map;
var chicago = new google.maps.LatLng(41.850033, -87.6500523);
/**
* The ZoomControl adds +/- button for the map
*
*/
function ZoomControl(controlDiv, map) {
// Creating divs & styles for custom zoom control
controlDiv.style.padding = '5px';
// Set CSS for the control wrapper
var controlWrapper = document.createElement('div');
controlWrapper.style.backgroundColor = 'white';
controlWrapper.style.borderStyle = 'solid';
controlWrapper.style.borderColor = 'gray';
controlWrapper.style.borderWidth = '1px';
controlWrapper.style.cursor = 'pointer';
controlWrapper.style.textAlign = 'center';
controlWrapper.style.width = '32px';
controlWrapper.style.height = '64px';
controlDiv.appendChild(controlWrapper);
// Set CSS for the zoomIn
var zoomInButton = document.createElement('div');
zoomInButton.style.width = '32px';
zoomInButton.style.height = '32px';
/* Change this to be the .png image you want to use */
zoomInButton.style.backgroundImage = 'url("http://placehold.it/32/00ff00")';
controlWrapper.appendChild(zoomInButton);
// Set CSS for the zoomOut
var zoomOutButton = document.createElement('div');
zoomOutButton.style.width = '32px';
zoomOutButton.style.height = '32px';
/* Change this to be the .png image you want to use */
zoomOutButton.style.backgroundImage = 'url("http://placehold.it/32/0000ff")';
controlWrapper.appendChild(zoomOutButton);
// Setup the click event listener - zoomIn
google.maps.event.addDomListener(zoomInButton, 'click', function() {
map.setZoom(map.getZoom() + 1);
});
// Setup the click event listener - zoomOut
google.maps.event.addDomListener(zoomOutButton, 'click', function() {
map.setZoom(map.getZoom() - 1);
});
}
function initialize() {
var mapDiv = document.getElementById('map-canvas');
var mapOptions = {
zoom: 12,
center: chicago,
/* Disabling default UI widgets */
disableDefaultUI: true
}
map = new google.maps.Map(mapDiv, mapOptions);
// Create the DIV to hold the control and call the ZoomControl() constructor
// passing in this DIV.
var zoomControlDiv = document.createElement('div');
var zoomControl = new ZoomControl(zoomControlDiv, map);
zoomControlDiv.index = 1;
map.controls[google.maps.ControlPosition.TOP_LEFT].push(zoomControlDiv);
}
initialize();
you can see a working example here:
http://jsfiddle.net/maunovaha/jptLfhc8/
To style the buttons without only using the API, so allowing you to do more things, create the button controls via the API - as described above in #Picard answer - and attach to the div a cssClass property, then style the button as you wish:
var zoomOutButton = document.createElement('div');
zoomOutButton.className = 'zoom-out-btn';
then to style you simply add your rules:
.zoom-out-btn{ /** your rules **/ }
same with zoomInButton:
var zoomInButton = document.createElement('div');
zoomInButton.className = 'zoom-in-btn';
.zoom-in-btn{ /** your rules **/ }
You'll probably need to use JQuery for this as there aren't really any unique classes or IDs for the control. I used a technique similar to here: Styling standard Zoom control in Google Maps v3
Here's my version:
google.maps.event.addListener(map, 'tilesloaded', function() {
$('#map_canvas').find('img[src="https://maps.gstatic.com/mapfiles/szc4.png"]').parent('.gmnoprint').css('YOUR_STYLE_HERE', 'YOUR_VALUE_HERE');
});
So basically, the above selects the zoom control image, and then steps up one level to get the entire container. You may need to specify a different image than I did, this was for the "Small" version.
I'm using Google Maps API v3. I would like to create a text overlay on a map that does not move when the map is panned. Is the best approach to manipulate the DOM elements accessible from the MapPanes object or is it best to create a custom control even though it would not do much other than display text?
The simplest way that I found worked for me was a few lines of JavaScript added after I created a new map. So, after this:
map = new google.maps.Map('myMapDivId', mapOptions);
add this:
var myTitle = document.createElement('h1');
myTitle.style.color = 'white';
myTitle.innerHTML = 'Hello World';
var myTextDiv = document.createElement('div');
myTextDiv.appendChild(myTitle);
map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(myTextDiv);
You will probably want to style the text to look nicer.
An alternative is to put the div in your HTML:
<div id="myTextDiv" style="color: white; position: absolute;">
<h1>Hello World</h1>
</div>
and then do this in your JavaScript:
var myControl = document.getElementById('myTextDiv');
map.controls[google.maps.ControlPosition.TOP_CENTER].push(myControl);
NOTE an important difference: If you use the HTML route to define your div, you must set the position style to absolute in the HTML to avoid rendering problems.
From you're describing, the best approach would be a custom control. Docs for that are here. Custom controls can be as simple or a complicated as you want.
One reason why you would want to muck around with the map panes is if you wanted such a 'control' to lie underneath the markers / shadows / polylines etc. I'm doing this right now to show a crosshairs in the center of the map at all times. But because I keep it as an overlay, I choose the panes in such a way that the markers are above it, so they can continue to be clicked and interacted with - using the mapPane. Here's how I'm doing it:
var CrosshairOverlay = function(map){
this._holder = null;
this.setMap(map);
};
CrosshairOverlay.prototype = new google.maps.OverlayView();
CrosshairOverlay.prototype.onAdd = function(){
var map = this.getMap();
var holder = this._holder = $('<div>').attr('id', 'crosshair')[0];
var crosshairPaper = this._paper = R(holder, 150, 150);
// ... all your drawing and rendering code here.
var projection = this.getProjection();
var wrappedHolder = $(holder);
var updateCrosshairPosition = function(){
var center = projection.fromLatLngToDivPixel(map.getCenter());
wrappedHolder.css({left:center.x-75, top:center.y-75});
}
_.each(['drag','dragend','bounds_changed','center_changed','zoom_changed','idle','resize'], function(event){
google.maps.event.addListener(map, event, updateCrosshairPosition);
});
google.maps.event.addListener(map, 'maptypeid_changed', function(){
_.defer(updateCrosshairPosition);
});
this.getPanes().mapPane.appendChild(holder);
};
CrosshairOverlay.prototype.draw = function(){
};
CrosshairOverlay.prototype.onRemove = function(){
this._holder.parentNode.removeChild(this._holder);
this._holder = null;
};
The reason the maptypeid_changed has its own handler with a defer is because that event is fired before the map properly sets itself up when changing the type. Just run your function after the current event loop.