I have a little Google Map app here: http://crocdoc.ifas.ufl.edu/projects/saveyourlogo/map/
This page is being embedded into the "Actions" tab on this page via iframe: http://saveyourlogo.org/en/programs/crocodile/american-crocodile/
Since that page begins with the "Actions" content div set to display:none, my map div (#map_canvas) starts out at size 0x0 and the map can't initialize properly. The Google Maps API says to simply use google.maps.event.trigger(map, 'resize'); once your map div is resized.
Since I don't have control of anything outside my little map's iframe, I can't attach the resize function to the "Actions" button. So I'm trying to detect a change in the #map_canvas div's size, and then fire the resize function. Since (I believe) you can't normally attach a resize event to a div, I've used Ben Alman's jQuery resize event script. (StackOverflow isn't letting me post more than two links, but it's easily Googled.) It is said to work with IE8, and I've seen that the examples on his site do.
It works perfectly in Firefox, but the resize event doesn't fire at all in IE8. (When I un-comment out the width alert, it does not display in IE8.)
function reset_map() {
google.maps.event.trigger(map, 'resize');
map.setCenter(center);
map.setZoom(zoom);
}
$('#map_canvas').resize(function() {
// alert(document.getElementById('map_canvas').offsetWidth);
reset_map();
});
Any idea why? Or perhaps suggestions for a more elegant solution?
UPDATE:
I've tried building my own little script that takes the offsetWidth of the div every 300ms, and then compares it to the last, but I just end up crashing the browser. Is there any way to detect this change from display:none to display:block without using a constant, quick loop? I know Ben Alman's script mentioned above uses one every 250ms.
Related
I am implementing a functionality where I can add, drag, and delete "sticky notes" on a webpage. The app is built in Vue js but it also renders content in an iframe. All the notes that are added to the page have to be on top of the iframe (which is almost 80% of the main .vue page), therefore positioning here is important and I also have to preserve the positions because I must render the notes on the same position on next page reload. The problem is that there's a lot of lag in dragging the "note" element.
The "sticky note" itself is a very lite weight separate component.
I have noticed that this because of the iframe that's present on the vue page because when I inspect the DOM in the browser and delete the iframe and then try dragging the "note" component then it works smoothly.
Things I have tried:
Use throttling: I tried to use loadsh.throttle but that does not make any difference.
Inject "sticky note" code inside the iframe: I tried to inject a sticky note element into the iframe and append it to iframe's body. It actually makes the dragging very smooth. But I don't want to go ahead with this solution because then I will have to write a lot of extra code to maintain the state of multiple notes (which can be done easily with Vue js).
In this solution, instead of using the document of the main page to attach events for dragging, I attached all the events to iframe.contentDocument.
So the question here is how do I make the drag smooth while using vue.js
Sandbox Link: https://codesandbox.io/s/affectionate-jang-3c1hw?file=/src/components/HelloWorld.vue
In this gif, the background is actually the iframe, I have reduced its opacity to hide it.
I could not actually include the iframe in sandbox code because of cross-origin problems but I have included a lot of extra content in that sandbox to make it heavy.
UPDATE:
Using the chrome task manager, I found that the page is only taking max 200MB memory and the GPU process is taking another 200MB. I am running this on a system with 16GB RAM. So I don't think it's a memory issue. But there is a sudden spike in the CPU consumption when I start dragging the element (up to 40%).
UPDATE:
I have found the fix for this problem. The actual problem wasn't lag but it was mouse trailing i.e. the draggable element wasn't able to catch up with the fast moving mouse cursor. And the slowness was due to e.preventDefault in the dragMouseDown method inside Note.vue. Just removing the e.preventDefault fixed all the problems. Also just adding a return false at the end of dragMouseDown method seems to cause the same amount of lag.
function dragMouseDown(e) {
e = e || window.event;
// e.preventDefault(); --> this line causes the mouse trailing issue
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
// return false; --> adding this line also causes mouse trailing problem.
}
So for now I have just removed the preventDefault from this function. But I tried searching and could not find any explanation of this behavior. Also I am not sure if not cancelling the event can cause any other issue.
The problem is that you're working with mousemove without using requestAnimationFrame to debounce. Here's a working example using debounce (CodeSandbox link)
Generally, anytime you're trying to animate with javascript, you want to use requestAnimationFrame. Another thing you could try is using transform to change the position of the element instead of absolute positions.
Here's the MDN reference on requestAnimationFrame.
Here's an article by Paul Irish about using transform instead of absolute positioning to speed up a drag/drop.
As your extra description in the comments,
It starts to lag when these a lot of other content on screen. There's
a v-for in the sandbox. make it do more iterations and it will start
to laggy
The problem you met is the Dom elements were too many on your page then caused high memory usage. (Actually I tried <div v-for="i in 10000" :key="i">", then it took around 3GBs of memory), finally, everything works slowly and laggy
If your page has tons of Dom elements, you may have to consider dynamically add only visible items into the Dom tree when scrolling.
Even there are some packages that already implements this feature.
Below is one demo which uses RecycleScroller of vue-virtual-scroller:
100K items in the Codepen
You will see even the number of the items are 100,000, it still works smoothly.
PS: you may notice the below statement in the user guide of the above package=vue-virtual-scroller
The browsers have a size limitation on DOM elements, it means that
currently the virtual scroller can't display more than ~500k items
depending on the browser.
I initialize 2 google maps inside of different tabs in a bootstrap modal popup.
The maps are initialized on shown event of the tab, and the maps are displayed correctly. There is a polygon of a field displayed correctly in the center of the maps.
After switching tabs multiple times, the map displays with the background blurred, but with the polygon still rendered correctly.
Once the maps have been rendered, I trigger the window resize event using the following code:
window.dispatchEvent(new Event('resize'));
It seems that the map sometimes refreshes correctly when switching back and forth between tabs, but only temporarily.
This functionality has worked for some time without problems, and has recently begun to show these symptoms.
Does anyone know if this is as a result of a google maps update? Or any ideas on how to refresh the map?
Thanks
I have tried delaying this call:
window.dispatchEvent(new Event('resize'));
with a setTimeout, and also tried firing the resize event on the map itself using:
google.maps.event.trigger(map, 'resize'); //I have read that this call is now retired.
I have tried setting the zoom level for the map after a tab is shown.
I am building a codepen at the moment, but am having trouble with google maps there too ;(
I expect to be able to switch tabs as many times as I like without the google map going blurry or getting re-centered or zoomed in/out.
Fixed - the issue was being caused by code that used to correct the problem of the map loosing position when hidden and shown by moving to a different tab:
google.maps.event.trigger(map, 'resize'); //I have read that this call is now retired.
and
window.dispatchEvent(new Event('resize'));
Removing these calls fixed the issue.
I'm using the UI-Calendar directive for Full Calendar and I have some resizing issues. The div that the calendar is in can change size based on an event which changes the div's class and thereby it's size. When this happens, the calendar redraws incorrectly. I can detect when the trigger occurs, but this seems to be inside the digest loop, i.e. before the element has actually changed sizes, so telling fullcalendar to redraw at this point is not helping.
Neither does jQuery.resize seem to fire (either by using angular's jqlite or the real jQuery). BTW, window resize does fire.
Currently, I'm solving it with a a $timeout every 100ms to check if the size has changed, which is a rather gross hack.
How can i either get notified when a div has resized or hook into the end of the digest loop, so that I see the size change reflected?
The resize event only fires when it's bound to the window (except for IE, where you can bind it to any HTML element). Here, more about that.
Checkout http://marcj.github.io/css-element-queries/ for an efficient solution.
I have some charts/graphs that I'm using the google visualization api to display. Initially i have set the display of their container to none. Once the user clicks a button I use javascript to make the container's display property to block. I'm seeing two strange behaviors when I do this
When I do it this way, the charts display improperly( they are smaller in size which causes some of the text labels to run over each other or off the chart). However, if I don't turn the display property to none initially then they work just fine.
When the charts are messed up and I press F12 (either on ie or chrome) to open the inspector, these charts magically redraw themselves to the proper size again.
Is there a way to either fix the 1st issue or somehow use javascript to emulate the redraw that is happening when I open the inspectors?
You can try following options:
1) Change your container div's display property to '' (empty parenthesis) instead of block
OR
2) After you change the display property to block, force the window resize event.
You could use the jQuery resize() method, like this:
$(window).resize();
Drawing charts inside hidden divs causes the Visualization API's dimension detection algorithms to break, which is why your charts are messed up. The fix is to draw the charts while the divs are visible, then hide the divs after the charts have drawn. You can use "ready" event handlers for your charts to accomplish this:
google.visualization.events.addListener(chart, 'ready', function () {
document.querySelector('#myChartDiv').style.display = 'none';
});
I tried to resize the window using scripts, but I found that some browsers did not support this. In the end, I ended up removing the container div from the DOM using jQuery and then appending it again when I wanted to display it. This preserved the correct sizes of the graphs
$tab3 = $('#tab3').remove();
then when I wanted to display it (the div of class panes is the original parent to the div of id tab3)
$('.panes').append($tab3);
I'm using a PolyCalc to polyfill CSS calc(). I need to run it whenever window size changes, but resize event seems not to be fired when scrollbars appear. And scrollbars appear with some delay on my page as some of the content is loaded from a server.
I guess I have to answer my own question. Thanks to #Tricky12 I know it is not likely that there is more elegant solution then checking for resize at some interval.
Anyway I found a jQuery resize plugin that does that and allows to check for resize on any element and also replaces default $(window).resize which then reacts on scrollbars.