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

Add bind:boundingClientRect #13412

Closed
nmzein opened this issue Sep 27, 2024 · 20 comments
Closed

Add bind:boundingClientRect #13412

nmzein opened this issue Sep 27, 2024 · 20 comments

Comments

@nmzein
Copy link

nmzein commented Sep 27, 2024

Describe the problem

I was working on creating user movable applets and came across many scenarios were I needed to reactively have the boundingClientRect of a div. This is not too hard to implement manually but would be really nice to have built into Svelte to avoid the boilerplate.

Describe the proposed solution

Add the ability to bind to the boundingClientRect of an html element using bind:boundingClientRect .

Importance

nice to have

@nmzein nmzein changed the title Add bind:clientBoundingRect Add bind:clientBoundingRect Sep 27, 2024
@Prinzhorn
Copy link
Contributor

@nmzein
Copy link
Author

nmzein commented Sep 27, 2024

That issue doesnt seem to mention boundingClientRect. I know that svelte already has bindings for contentRect among other things but these do not provide the same functionality. Unless I'm mistaken

@Prinzhorn
Copy link
Contributor

Can you explain what exactly the difference is for your use-case?

@nmzein
Copy link
Author

nmzein commented Sep 27, 2024

Im not aware of any of the existing bindings giving absolute x, y positions of the bounded to div. ContentRect just gives the dimensions of the div without taking into account its position at all. Binding to boundingClientRect would also allow you to get the clientWidth and clientHeight at the same time as the absolute x and y pos without making extra bindings for those separately. This is another area where contentRect differs from boundingClientRect where contentRect doesnt include the padding in the width and height of the object which forces you to either calculate it or use clientWidth and clientHeight bindings.

@dummdidumm
Copy link
Member

This sounds like something you can solve through an action.

@nmzein
Copy link
Author

nmzein commented Sep 27, 2024

Which is what I did eventually. Thought it would be nicer to have this built-in though since it can be confusing as to why it doesn't exist. If theres no drawbacks to having this included is there a reason not to?

@Prinzhorn
Copy link
Contributor

If theres no drawbacks

Don't you need to poll this? How did you implement it? Maybe having this as a binding is a footgun when you should only call getBoundingClientRect when you need to.

@brunnerh
Copy link
Member

Don't you need to poll this?

Not necessarily, the existing bindings use a ResizeObserver if possible.

@Prinzhorn
Copy link
Contributor

Prinzhorn commented Sep 29, 2024

Not necessarily, the existing bindings use a ResizeObserver if possible.

getBoundingClientRect depends on the scroll position (among others), so you'd need to poll it or imperatively call it when you need it (I wouldn't poll it with raf). You can't solely rely on ResizeObserver. That's what I meant with "having this as a binding is a footgun". It needs to be used carefully depending on the requirements.

@nmzein
Copy link
Author

nmzein commented Oct 6, 2024

Yes you do have to poll because ResizeObserver would only run when the div was resized not when it was dragged and moved. I personally think even if its a "footgun" it should be available since if people really need it they will create it themselves anyways. Polling does not create that bad of a performance penalty unless used in excess. IMO this is where documentation steps in to tell people to use things carefully.

@nmzein nmzein changed the title Add bind:clientBoundingRect Add bind:boundingClientRect Oct 6, 2024
@brunnerh
Copy link
Member

brunnerh commented Oct 6, 2024

The size bindings also used polling as a fallback (but will switch to ResizeObserver permanently in v5).
The polling was only initiated if a binding existed, so it's not degrading general performance.

@nmzein
Copy link
Author

nmzein commented Oct 7, 2024

Yeah but boundingClientRect doesnt only give you size information but positional. I dont believe a ResizeObserver would be able to detect changes to position no? Please correct me if I'm wrong.

@brunnerh
Copy link
Member

brunnerh commented Oct 7, 2024

Yes, I was just pointing out the precedent for polling and its implications.
As @Prinzhorn already noted, an observer would not be enough here, unfortunately.

@nmzein
Copy link
Author

nmzein commented Oct 7, 2024

Right. So what I'm getting is you wouldnt be against adding this even though its implemented using polling right? If thats the case I could maybe try to navigate myself through the source code and start figuring out what needs to be added.

@brunnerh
Copy link
Member

brunnerh commented Oct 7, 2024

I'm not a maintainer, @dummdidumm is, and he suggested to use an action.
With Svelte 5 coming up, I'm inclined to agree.

In earlier versions, the ergonomics of having an action that updates a value were not great. Usually you would have to do things like using a separate store or maybe pass a setter function around.

With runes you can create a utility that hosts an action and the value. So the end user code really does not change all that much, e.g.

<script>
+	import { createClientRectTracker } from './client-rect-tracker.svelte.js';

-	let clientRect = $state(null);
+	const clientRectTracker = createClientRectTracker();
</script>

- X: {clientRect?.x} <br>
- Y: {clientRect?.y} <br>
+ X: {clientRectTracker.value?.x} <br>
+ Y: {clientRectTracker.value?.y} <br>

- <div bind:boundingClientRect={clientRect}> ... </div>
+ <div use:clientRectTracker.action> ... </div>

Example action (Try this in Firefox. Chromium throttles the polling due to some broken out-of-focus detection or something like that.)

With a separate action you also have more control, you can e.g. provide an option to set the polling interval.

@nmzein
Copy link
Author

nmzein commented Jan 29, 2025

Hey so I came across this implementation in the runed package that uses only a ResizeObserver and a MutationObserver. Am I missing something or does it seem doable without polling? https://github.com/svecosystem/runed/blob/main/packages/runed/src/lib/utilities/element-rect/element-rect.svelte.ts

@Prinzhorn
Copy link
Contributor

Prinzhorn commented Jan 29, 2025

There are countless ways you can break this:

  1. Put a second element above the element
  2. Change the height of this element
  3. Not updating
Screencast.from.2025-01-29.10-21-04.mp4

Zooming in your browser also breaks it.

@nmzein
Copy link
Author

nmzein commented Jan 29, 2025

Oh ok so its a broken implementation. Sounds good thanks.

@nmzein nmzein closed this as completed Jan 29, 2025
@brunnerh
Copy link
Member

brunnerh commented Jan 29, 2025

I think that might work if the entire window is watched deeply for mutations, for resizes & scroll as well, not sure if that has a significant performance impact.

@nmzein
Copy link
Author

nmzein commented Jan 29, 2025

That sounds like it would be worse than polling lol.

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

No branches or pull requests

4 participants