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.
Related
I know that this is not a 'good question' to poseābut I'm desperate by now and need to find some inspiration.
I have a questionnaire page/web-app that uses CSS transitions to slide to the next page of questions/options.
It is well-tested and the transitions in question aren't complex.
But the transition to one special page went out of control recently. And although I consider myself well versed in CSS and JavaScript I'm totally lost with this problem.
The construct in question works like this:
I have a 'window' that is positioned absolutely and has its overflow hidden.
Inside that is a page container positioned relative to no left or top values or transforms given.
The pages inside this container are positioned absolutely according to their status classes positioned on the left: 0 (active), left 100% (not yet shown), and left: -100% (already done)
What happens now is that when I switch classes to show a certain page, the page container 'jumps to the left'. In Chrome nearly -100% in Firefox about -70% same in Safari.
Those values are my guess because when I open the dev-tools, nothing is to be found in the styles, in the computed values anywhere.
When I move back to the previous page the container jumps back to its original position, and if I move again to the critical page it stays.
I do no positioning whatsoever with JavaScript anywhere, I only switch classes on HTML elements.
After hours of experimenting back and forth, I found out that the problem is in some way JavaScript-related anyhow.
Using the exact same CSS and HTML with a slightly older version of JavaScript does not show the strange behavior. The changes in the Script are mostly ES2020-module related, and the new version does nothing even remotely related to DOM manipulation different from the older working version.
When not using transitions, the shift of the container does not occur.
Has anyone come across something similar?
Does anyone know of other tools to deeper analyze the current layout state of a page more than the respective developer tools of the browsers?
Any other ideas?
Today I found a solution and at least some kind of explanation, but I still have to investigate some more to truly understand the why and how. I'll post an update if I find a better explanation.
The problem has to do with a text field and focus.
The page in question has a text (search) as its main component.
[I am aware and always was that browsers try to move focused elements into view, regardless of what the author's CSS says.
Therefore, when I decided to give focus to the text field which it does not have on its own, as every page movement required a button to be clicked, I did so on transitionend. This has never changed.
When the issue first occurred, one of the things I tried first was to disable that (auto) focus behavior. It did not solve the problem.
I cannot say what made that change in behavior happen. I change the construction of the page to include a grid some time before the issue occurred. That in itself was not the reason, however (it worked for quite some time with that system, and removing it did not sole the behavior).]
What solved the issue was to initially fully disable the text field and only enable it on transitionend (then focusing it).
The reason browsers moved the page to different positions likely has to do with this situation, as the text fields width is ch based.
The important takeaway (for now) for me is, that there is no hint to be found in the developer tools when the browsers moves - elements in the (in my case unsuccessful) attempt to keep inputs in the viewport.
I think that this should be changed/fixed.
I will as soon as I have the time try to better understand the things that made the problem occur, If someone has information or experience with similar situations pleas add useful hints and background info.
As you can see from the screenshot that Layout is triggered on root element (#document) in every frame which causes next operations like paint.
This Layout is caused by Recalc Style which I am not able to understand why it's happening. There's no script running. Just Recalc Style > Layout > ...
I need to know why such Recalc Style could occur without script?
Additional Information:
This page is using Semantic UI framework
It contains a custom grid
This Recalc Style > Layout operation is performed in ever frame even when the page is idle.
First, is this even a problem? Unless you're experiencing performance issues, don't waste time trying to prematurely optimize.
Next, enable the Chrome rendering settings to show a what areas of the screen are GPU layers and what parts are being repainted.
Style recalculation and painting can happen for many reasons that have nothing to do with javascript. Browser resizing, tables performing rel-layout as new items are loaded during the page load, etc. Since you didn't provide any information about the page that's being loaded, it's hard to say.
My guess would be that you've got some kind of CSS animation that's being applied to a property other than translation, rotation, scale or opacity. All of those properties are GPU accelerated and won't trigger repaints, however, fading background colours, changing border thicknesses, even adjusting top, left, right and bottom properties all cause repainting.
You've mentioned that you're using Semantic UI. Either Semantic or one of its themes are causing this redraw to happen. Your screenshot doesn't actually prove that scripts aren't being called.
So I have this issue with something like old deprecated <marquee>. Here's fiddle: https://jsfiddle.net/qbqz0kay/1/
It's one (and simpliest) of hundreds of attempts. I can't resolve main issues:
how to remove the gap between end and (new) beginning of the list (it should be like one infinitely scrolling sentence). I've tried with removing first li elements and adding them to the end but it affected overall dimensions of the list and in consequence - the animation was disrupted.
part of the list will be changing once in a while (site is connected to websocket) and every change in its content affect dimensions of the list also. So again - problem with stuttering animation occured.
Any ideas how to handle this? I've seen many ready-made examples but none of them handles those two issues.
Marquee can't help you in this case, as you can't achieve continuous scrolling using this element. Instead, just a bit of javascript might do the trick here. I tried this example which is also very well documented on my own blog and worked perfectly fine, more on this article:
http://www.dynamicdrive.com/dynamicindex2/crawler/index.htm
This onscreen plugin is meant to add classnames only to elements when they are within the viewport, however it seems to be adding the classnames as soon as the page is loaded which isn't what's meant to happen.
$(function() {
setInterval(function() {
$("#star").filter(":onScreen").addClass('animated bounceInRight');
}, 0)
})
I'm using it within this plugin which, is a book effect plugin and I think it conflicts with onscreen in some way: http://pastebin.com/UmyJ6zBW This plugin requires different sections on one page for it to work. Onscreen works when used within the first section but breaks when in others that aren't actually on view. How do I fix this?
You can do one of three things.
Don't bother. In general you don't need to worry about the style of elements that are not in view.
Use setInterval() but reduce the interval to the minimum acceptable value but no less than about 20 ms.
[preferred] Establish an event handler that fires in response to whatever causes your #star element to be moved on/off screen.
For the third approach :
jQuery animations provide for a step callback which fires as each animation step is executed.
As scrolling the document will also cause elements to come in/out of view, you probably also want to a scroll handler.
With care, you should be able to write a single function and use it for both purposes.
I've made a slideshow using JQuery which at the moment just uses append() to add an <img> to a container that was previously defined. The slideshow automatically rotates through, returning back the the start after the final slide and continuing infinitely.
At the moment, I don't remove the previous image in the container at all. I haven't noticed anything change in terms of performance or my memory filling up - a behaviour that I'm used to in Flash if I were to continue adding elements without removing old ones.
Is it necessary to remove the old images, or are these never going to cause performance issues?
Some of this might be browser-dependent, but in general I would say that yes it is indeed "bad" not to clean up your old elements. Whether or not an element is visible, it is still adding a node to the structure of the DOM, and you cannot add to the DOM infinitely.
Unless perhaps the browser implements some sort of intelligent pruning algorithm that swaps out unused/non-visible portions of the DOM, but I've never heard of such a thing being done in practice.
Most likely, it will affect performance over time, the more photos added, the more the performance will suffer, but if at all possible, test it out, add as many as you can, but if there is not a whole lot going on in your site, you may not see any performance issues. if you don't see any issues, don't worry about it. like i said, try it out.
Shouldn't adding $(div).innerHTML='' before appending the image clear everything off in the div?
The browser caches any images that are loaded. Each image does consume memory but removing the images won't affect the cache. If the cache starts to become a problem, the browser will start freeing up memory. Each browser does this differently and some do it better than others. Any image that is not visible in the viewable space of the current page is fair game. If an image on the current page is thrown out and subsequently made visible in the viewable area of the page it is reloaded.
In summary, removing the old images won't affect performance.