Google maps bounds/zoom not working when parent div is hidden - javascript

I have a large page of user data that is formatted for ease of use using accordion areas.
Click on the accordion control and the relevant information is shown. Click it again and it is hidden.
One of these accordions has a google map inside it with multiple pins.
When the parent accordion div is hidden bounds/zoom has no effect. When it is visible bounds/zoom works.
I can only imagine that the DOM has the collapsing parent div set to zero height and therefor Google Maps 'sees' this as zero all the way down the stack and sets bounds based on a zero height and width accordingly.
Here is a fiddle:
https://jsfiddle.net/wjebusfL/1/
The default is to have the accordion closed. When you click the open button you will see that the map contains two markers but the zoom is way off.
If you comment out the display and opacity lines in the css for .accordianarea
display: none;
opacity: 0;
You will see the accordion in an open state by default and the map bounds/zoom working OK.
Is there any way to 'force' the google map to render correctly with the accordion closed?

New answer:
After digging a little deeper, I've found that the MapOptions has a minZoom option:
minZoom (optional)
The minimum zoom level which will be displayed on the map. If omitted, or set to null, the minimum zoom from the current map type is used instead.
If we check your map_zoom() function, it's returning 8 on the first call.
If we use that 8 together with minZoom (instead of regular zoom) we'll get the desired result without calling map_zoom() a second time:
var defaultZoom = 8;
var allMarkers = new Array();
var mapOptions = {
minZoom: defaultZoom,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
Updated JSFiddle
Old answer:
Move the map_zoom() outside of map_init() so we can call it from anywhere
On $('.accordiancontrol').click, call the map_zoom() to update our zoom:
$('.accordiancontrol').click(function(){
if ($('#accordianarea' + thisId).is(":visible")) {
// Close accordianarea
}
else {
// Open accordianarea
// Update zoom
map_zoom();
}
});
Updated JSFiddle

"I can only imagine that the DOM has the collapsing parent div set to zero height and therefor Google Maps 'sees' this as zero all the way down the stack and sets bounds based on a zero height and width accordingly."
As you stated, zoom sets bounds based on a zero height and width.. What you can do is to call map_init(); on map show. That way div has height and width and map gets proper zoom bounds
$('.accordiancontrol').click(function(){
var thisId = $(this).attr('id').replace(/^\D+/g,"")
if ($('#accordianarea' + thisId).is(":visible"))
{
// Close accordianarea
}
else
{
map_init();
// Open accordianarea
}
});

Related

Webcomponents google-map displays gray area to the right after hiding app-drawer

I have a <google-map> inside a <app-drawer-layout>, and it works fine except the map displays a gray area to the right when the drawer is hidden.
All the discussions I've found about this issue say that the solution is to trigger the resize event of the map, but it doesn't work for me. I even tried adding a delay just to be on the safe side, but still it does nothing.
document.querySelector('app-drawer').addEventListener('app-drawer-transitioned', function(){
window.setTimeout(function(){
console.log( map.clientWidth );
google.maps.event.trigger(map, 'resize');
}, 100);
});
The console output confirms that the map width has indeed changed before triggering the resize event, but the gray area persists. Even if I pan and zoom the map, the gray area is still there (although it becomes wider or narrower depending on the pan). The only thing that seems to correct it is resizing the entire browser window.
This is in Chrome on Mac btw.
Turns out that I was trying to trigger the resize event on the DOM-element instead of the Maps API object. The Maps API object can be obtained via the .map property of the <google-map> DOM-element.
This works for me
<script>
var mapElem = document.querySelector('google-map');
mapElem.addEventListener('api-load', function(e) {
var mapObj = mapElem.map;
document.querySelector('app-drawer').addEventListener('app-drawer-transitioned', function(){
google.maps.event.trigger(mapObj, 'resize');
});
});
</script>

Google Maps API showing grey map on ajax page load

I'm using ajax to load (into a div) the page that includes the Google map, however when it's finished loading it shows only a grey background for the map, with a Google logo in the bottom left, and a "Terms of use" link in the bottom right.
If I resize the window, then the map instantly appears. Also if I reload the entire page by pressing F5 the map loads normally. It's only when I initially load this map page using jQuery ajax that I get the grey map.
On the container page I load the maps api.
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?sensor=false"></script>
On the map page I do everything else.
$(document).ready(function() {
initializeMap();
});
function initializeMap() {
var companyname = document.getElementById("companyname").value;
infowindow = new google.maps.InfoWindow();
latlng = new google.maps.LatLng(-34.397, 150.644);
var mapOptions = {
zoom: 13,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
geo = new google.maps.Geocoder();
map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
bounds = new google.maps.LatLngBounds();
resultsPanel = new google.maps.DirectionsRenderer({suppressMarkers:true});
resultsPanel.setMap(map);
$("#progressbar").progressbar(0);
var companyaddress = getCompanyAddress();
setCompanyMarker(companyaddress,companyname)
}
On resize I recalculate the height so that it fills the full page. If I don't do this then the map opens with 1px height, though I don't know why. Could that be the same issue?
function calculateHeight(){
var height = $('#maincontent').height();
$('#map-canvas').css("height",height);
}
$(window).resize(function () {
calculateHeight();
});
Since the map appears when the user resizes the window manually I thought programatically calling the resize event would have the same effect, so I added this:
$(window).trigger('resize');
But that did nothing.
I tried moving the api script to the map page but that caused a "You've loaded the same script multiple times" error if I load the page more than once, which then triggered other errors. However, with this setup I was able to load the map successfully using ajax after three page loads. In other words, after loading the container page once, I have to click on the menu item (that loads the map page) three times to get it to load properly. the first two times it still fails to load properly, but after that it loads properly every time, though with a load of JavaScript errors.
I also tried loading the api script asynchronously as per this suggestion.
I also tried adding "async defer" to the api script as per this suggestion.
Neither of these suggestions worked, and now I'm here.
What is causing the map to load with a grey background when the page is loaded via ajax, and what can I do about it?
1.Make sure map-canvas have width set in CSS.
2.When exactly did you try calling google.maps.event.trigger(map,'resize'); as geocodezip suggested? You need to call it after any map-canvas div's dimension change (calculateHeight) or any change to visibility of maps div. If the map-canvas div, or it's parent, or it's parent's parent (any predecessor) has display:none set at some point, the map view won't initialise properly and you will only see gray map. Also add the map resize code into some timeout to be sure, and then lower it if it works, e.g:
function calculateHeight(){
var height = $('#maincontent').height();
$('#map-canvas').css("height",height);
setTimeout(function(){ //also do this after any other visibility/dimension change
google.maps.event.trigger(map,'resize');
}, 500);
}
Try this, which worked for me
google.maps.event.addListener(map, 'idle', function()
{
google.maps.event.trigger(map, 'resize');
});
map.setZoom( map.getZoom() - 1 );
map.setZoom( map.getZoom() + 1 );

How can I create overlapping images that reveal themselves as you scroll

I am looking to create a scrolling effect similar to that shown here: http://www.seaham-hall.co.uk/
However I am unable to achieve the desired effect, and inspecting the sites code gives me no hints. Quite difficult to google for as it is also quite difficult to describe. The closest I can get to finding a solution is this JSFiddle:
http://jsfiddle.net/xtyus/1/
(function($){
/* Store the original positions */
var d1 = $('.one');
var d1orgtop = d1.position().top;
var d2 = $('.two');
var d2orgtop = d2.position().top;
var d3 = $('.three');
var d3orgtop = d3.position().top;
var d4 = $('.four');
var d4orgtop = d4.position().top;
/* respond to the scroll event */
$(window).scroll(function(){
/* get the current scroll position */
var st = $(window).scrollTop();
/* change classes based on section positions */
if (st >= d1orgtop) {
d1.addClass('latched');
} else {
d1.removeClass('latched');
}
if (st >= d2orgtop) {
d2.addClass('latched');
} else {
d2.removeClass('latched');
}
if (st >= d3orgtop) {
d3.addClass('latched');
} else {
d3.removeClass('latched');
}
if (st >= d4orgtop) {
d4.addClass('latched');
} else {
d4.removeClass('latched');
}
});
})(window.jQuery);
However I am not sure that is going in the right direction, this pulls images up and covers the previous image, but notice on the Seaham Hall site the images don't appear to move up at all, they are stationary and become revealed as you scroll.
How do I recreate this effect? My initial thought was to have the first image shrink as you scroll from 1000px down to 0px, and the second image grow to 1000px, and as you continue to scroll this image then shrinks and the third grows, and so on. However this means that after the first image all the other images have a starting size of 0px and there would technically be no scrolling on the page to begin with, so that is an issue.
My second thought is that perhaps the second image is fixed to the page, the first image slides up revealing the second as you scroll, the second image would not appear to move. Once the first image has gone off the top of the page the second image is detached from the page and allowed to move up with scrolling, while the third image is attached and revealed as the second moves up, this would give the exact effect seen in the Seaham website but I have no clue of it is the correct answer.
If anyone can point me to tutorials or a JSFiddle with a basic concept I can probably figure it out from there. Just stumped what direction to approach this from.
That's a nice effect. Here's one way to do it.
Put each image in a fixed position div, which takes up the entire viewport (initially) and has overflow:hidden.
Set each div's z-index to be higher than the next div's.
As the window scrolls, adjust the height of the divs as a function of the window height times the div's position (index) in the DOM, minus the window's scrollTop:
$(window).scroll(function() {
$('.D').each(function(index) {
$(this).css({
height: $(window).height()*(index+1) - $(window).scrollTop()
});
});
});
Additional content will need a higher z-index than the image divs. And note that z-index works with positioned elements only.
Fiddle
Your desired effect isn't technically a parallax background, but it's close enough that parallax jQuery frameworks should work for you.
I would suggest you research jQuery Parallax plugins as they'll likely provide the functionality you'd like without much custom work. Of course since you're dealing with large images it's also best to keep an eye on the resource management; a good plugin should be fairly efficient but others may be slow or resource intensive.
Check this jquery plugin:ScrollMagic
usage: taken from github
The basic ScrollMagic design pattern is one controller, which has several scenes attached.
Each scene has a definite start and end position and defines what happens when the container is scrolled to the specific offset.
/*
Basic workflow example
*/
// init controller
var controller = new ScrollMagic();
// assign handler "scene" and add it to controller
var scene = new ScrollScene({duration: 100})
.setPin("#my-sticky-element") // pins the element for a scroll distance of 100px
.addTo(controller); // add scene to controller
// adding multiple scenes at once
var scene2 = new ScrollScene();
var scene3;
controller.addScene([
scene2,
scene3 = new ScrollScene({duration: 200}), // add scene and assign handler "scene2"
new ScrollScene({offset: 20}) // add anonymous scene
]);

Resizing a leaflet map on container resize

I have a <div> containing a leaflet map. Upon certain events the height of the <div> will be altered. I'd like for the map to resize to the new dimensions of its surrounding <div> so that the old center is centered in the resized smaller or larger map. I tried using the invalidateSize() function, but it doesn't seem to work at all. How can I resize and center the map after that map-container-resize event?
$mapContainer.on('map-container-resize', function () {
map.invalidateSize(); // doesn't seem to do anything
});
Edit to give more context:
The map container is styled initially as
#map-container {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
transition: height 0.5s ease-in-out;
}
After a user clicks a certain button, another panel shows at the bottom of the page and the map-container's height will be reduced to something less than 100% (say 80%).
Upon click on this button, the map-container-resize event is triggered so that I can make the map resize and center on its old (i.e. before the resizing happened) center. The map itself should then also be resized to 80% of its initial height.
The APi doc for invalidateSize seemed to be what I wanted:
"Checks if the map container size changed and updates the map if so
[...]"
But having a look with the output of the getSize function before and after the call to invalidateSize, nothing is different, the map remains at its old size.
The problem is that the resizing of the #map-container div is done via a css transition. The transition hasn't started yet, let alone ended, when the call to invalidateSize happens so the leaflet map cannot recognize any change of dimensions of its surrounding div.
Triggering the map-container-resize event with a delay solved the problem. This way :
setTimeout(function(){ map.invalidateSize()}, 400);
L.Map.invalidateSize() only informs leaflet map object that its container size has been changed, and therefore is should draw less or more map tiles. It does not actually change any dimensions, e.g. of its containing <div>, and does not move the map. You should do it yourself.
I came across this question today and wanted to provide an updated answer based on 2020 Browser API. This example uses the Browser's ResizeObserver to monitor the size of the div that Leaflet is mounted too. Assuming the following HTML Snippet:
<div id="map" />
With the following JavaScript:
const mapDiv = document.getElementById("map");
const map = L.map(mapDiv).setView([51.505, -0.09], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
const resizeObserver = new ResizeObserver(() => {
map.invalidateSize();
});
resizeObserver.observe(mapDiv);
This should monitor the map div, and call the invalidateSize() method on the Leaflet map when the map div size changes. This approach allows you to handle the resizing "closer" to the map code, rather than trying to rely on window resize events or listening for changes triggered elsewhere in the application.
Obviously the CSS for the map div itself will need to ensure that it resizes in whatever way you want it to. This code snippet will ensure the Leaflet is appropriately updated when that happens.
You can use below code after resize that
map.invalidateSize()
https://github.com/Leaflet/Leaflet/issues/690
the accepted answer is a bit hacky in that it relies on the sleep being longer than the transition.
I have found this to work well:
$("body").on($.support.transition.end, '#main-navbar .nav-collapse', function(event){
console.log("end of the animation");
});
Just call resize window event rather than timing the map to load.
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
window.dispatchEvent(new Event('resize'));
// Triggers a window resize
// Thus your map automatically triggers invalidateSize().
Ran into this problem running VueJS, Leaflet 1.2.0. The resizing didn't appear complete as others mentioned above. My solution within VueJS was to call the nextTick function:
var vm = this
var container = vm.$refs.container
vm.mapStyle.width = `${vm.getElementContentWidth(container)}px`
vm.mapStyle.height = `${vm.getElementContentHeight(container)}px`
vm.$nextTick(() => {
if (vm.map) vm.map.invalidateSize()
if (vm.layerBase) vm.layerBase.redraw()
})
I believe pure javascript would be

CSS, Jquery - Width of absolutely positioned div returned as 0

I'm trying to run some conditional javascript to position a div such that, if a marker is clicked that is on the right hand side of a photo, the marker tooltip will be positioned to the left of the marker, otherwise it will be to the right of the marker. So, if the marker is on the right, I instruct the tooltip to be positioned at the marker's position().left, minus the width of the tooltip. However the tooltip width always changes, depending on the text inside, and calling jquery's width() function does not return a proper width. Any ideas on how to fix this? Here's the example:
http://bit.ly/IkbWl6
and here's my code:
var tagPos = $photo_tag.position();
if (isNarrow == true){
var photoWidth = $('#photo_box').width();
if (tagPos.left > photoWidth/2){
var brandWidth = $('.brand_name').width(); //MH - NOT WORKING
$brand_name_container.css({'left' : tagPos.left-brandWidth, 'top' : tagPos.top+brandAdjTop});
} else {
$brand_name_container.css({'left' : tagPos.left+brandAdjLeft, 'top' : tagPos.top+brandAdjTop});
}
}
Just in case, but is your tooltip visible at the moment you request it's width ?
The tooltip must be visible to get rendered by the render engine, without that it won't have any size.
You may do something like that for example
$('#mytooltip').show(0);
width = $('#mytooltip').width();
$('#mytooltip').hide(0);
this will be sufficient for the rendering to happen but it won't have the time to be really displayed to the users.

Categories