Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve performance of bind:clientWidth using ResizeObserver api #7583

Closed
Gin-Quin opened this issue Jun 4, 2022 · 4 comments
Closed

Improve performance of bind:clientWidth using ResizeObserver api #7583

Gin-Quin opened this issue Jun 4, 2022 · 4 comments
Milestone

Comments

@Gin-Quin
Copy link

Gin-Quin commented Jun 4, 2022

Describe the problem

The bind:clientWidth directive is very useful when you need to programmatically control the size of an element, but its current implementation suffers heavy performance issues.

Presently, it seems that an iframe is created to compute the element size. Adding a new element like an iframe is not a light operation, and if a developper starts to setup bind:clientWidth everywhere, its application will become very laggy.

There exists is an official and quite simple API to deal with these kind of situations: the ResizeObserver API. Its purpose is to listen to any change of the element size and trigger a callback function.

I tested the performance some time ago compared to Svelte's current approach, and when having a lot of bind:clientWidth, the performance gain is huge.

Describe the proposed solution

I am personally using these 24 lines of code instead of bind:clientWidth to create a directive named resizeObserver:

let observer: ResizeObserver;
let callbacks: WeakMap<Element, (element: Element) => any>;

export function resizeObserver(element: Element, onResize: (element: Element) => any) {
   if (!observer) {
      callbacks = new WeakMap();
      observer = new ResizeObserver(entries => {
         for (const entry of entries) {
            const onResize = callbacks.get(entry.target);
            if (onResize) onResize(entry.target);
         }
      });
   }

   callbacks.set(element, onResize);
   observer.observe(element);

   return {
      destroy: () => {
         callbacks.delete(element);
         observer.unobserve(element);
      },
   };
}

Then you can use it this way to mimic bind:clientWidth but without having to create an iframe:

<script lang="ts">
   let clientWidth = 0
</script>

<div use:resizeObserver={element => clientWidth = element.clientWidth}>
   My width is: {clientWidth}
</div>

Alternatives considered

Checking size changes is tricky. The ResizeObserver api is native and well-supported by browsers so I see no reason to not use it.

Importance

nice to have

@brunnerh
Copy link
Member

brunnerh commented Jun 4, 2022

If this is implemented internally in bind:clientWidth, ResizeObserver could be used by default with a compiler/<svelte:options> flag to fall back to the current implementation if really necessary.

@dummdidumm
Copy link
Member

Pending PR: #5963

dummdidumm added a commit that referenced this issue Apr 11, 2023
Implements ResizeObserver bindings: #5524 (comment)
Continuation of: #5963
Related to #7583

---------

Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
dae added a commit to ankitects/anki that referenced this issue Dec 8, 2023
Profiling revealed that binding to clientWidth for each component in
the deck options was taking up a sizable amount of time, due to the
inefficient way it's implemented: sveltejs/svelte#7583

In one case, we can instead defer checking clientWidth until we go to
display the popover. In the other case, we can achieve the revert button
positioning a different way.

The last change dropped the speed index in Chrome from 1.4s to 1s; this
change brings it down to 0.7s.

Also fixed the hovered select element from jiggling due to a different
border width when hovering.
@samal-rasmussen
Copy link

This was addressed in this pr: #8022

@dummdidumm dummdidumm added this to the 5.0 milestone Jan 23, 2024
@dummdidumm
Copy link
Member

Svelte 5 will use the resize observer API for these bindings

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants