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

Dimension bindings intermittently add inline style="position:relative;" in Firefox #4776

Closed
niclasmattsson opened this issue May 4, 2020 · 19 comments

Comments

@niclasmattsson
Copy link

niclasmattsson commented May 4, 2020

Using dimension bindings on an element will sometimes add an inline style position:relative; in Firefox. This overrides any other CSS positioning that may be defined on the element. The problem doesn't happen in Chrome.

The code below (borrowed from w3schools.com) reproduces the problem on my system roughly 50% of the time (although you may have to reload 10-20 times to see it, maybe due to page caching). I'm using Svelte 3.22.1 with Rollup, Firefox 75, Windows 10.

The inner div should be positioned to the right side of the outer div. When Svelte sometimes gives it position:relative; it is positioned to the left.

<script>
    import { onMount } from 'svelte';

    let clientWidth, clientHeight;
</script>

<style>
    .relative {
        position: relative;
        width: 400px;
        height: 200px;
        border: 3px solid #73AD21;
    } 

    .absolute {
        position: absolute;
        top: 80px;
        right: 0;
        width: 225px;
        height: 100px;
        border: 3px solid #73AD21;
    }
</style>

<h2>position: absolute;</h2>

<p>An element with position: absolute; is positioned relative to the nearest positioned ancestor (instead of positioned relative to the viewport, like fixed):</p>

<div class="relative">This div has position: relative;
    <div class="absolute" bind:clientWidth bind:clientHeight>This div has position: absolute; and right: 0;</div>
</div>

EDIT: I can't reproduce this problem in Svelte's REPL. This is another sign (along with the intermittency) that makes me think the bug is lifecycle related. If so then maybe the page loading delays caused by the REPL infrastructure avoid the problem.

@Conduitry
Copy link
Member

I'm not clear on the inner workings of the resize listener, but setting position: relative on it when its computed style is position: static is part of the mechanism implemented in #984/#1386. I don't know whether there's something that could be done about that.

@tivac
Copy link
Contributor

tivac commented May 4, 2020

It's so the injected <iframe> that is used to monitor for the element's size changing can use absolute positioning to be the exact same size as the element being measured.

@Conduitry Conduitry added the docs label Jun 8, 2020
@Conduitry
Copy link
Member

Labeling this as docs because we should probably simply be documenting the expected gotchas here with the technique Svelte uses for this.

@niclasmattsson
Copy link
Author

Fine. Label it however you see fit, but please note that:

  • This only happens in Firefox, not in Chrome.
  • The behavior is intermittent and the problem only appears half of the time.

This makes me suspect a bug rather than an expected gotcha, but you guys are the experts.

@JakubBlaha
Copy link

This also happens in the latest Edge version built on Chromium. The position: relative seems to happen every time, but the value that comes from the bound properties doesn't seem to update every time properly. If I then remove the position: relative using the Edge developer tools, the values of the bound properties update correctly.

@JakubBlaha
Copy link

Okay so...
it seems like the clientHeight does update correctly when my mouse cursor is outside of the viewport and doesn't update when my mouse cursor is inside the viewport during the site load.

What I have done as a workaround is that I have bound both, the clientHeight and the element's this.

<div
    class="w-full h-full overflow-scroll"
    bind:clientHeight={containerClientHeight}
    bind:this={container}
>

And then I have used onMount and tick to update the containerClientHeight variable to the correct value.

onMount(async () => {
    await tick();
    containerClientHeight = container.clientHeight;
})

Now the containerClientHeight variable seems to update correctly every time..

@gorsat
Copy link

gorsat commented Jun 5, 2021

I have been chasing down an intermittent problem on Safari where a sticky header was rendering incorrectly.

I found that the problem was caused by "element.style {position: relative;}" on the element that was supposed to be sticky. I could not see anything in our code that could be causing this. Finally, I looked at bundle.js and found it coming from svelte's generated code. A quick search then brought me to this issue thread. And, yes, the element in question is using the dimension binding feature (for offsetHeight).

To fix the problem I added an onMount handler for the parent component that just does element.style.position="" to clear out whatever svelte might have put there. Now the element properly behaves using the position: sticky property set in css.

This seems to me to be a real bug. Imo, svelte should never be setting the position property of one of my elements under the covers. But if it's going to do that then it should at least do it in a predictable, consistent and well documented way.

@isaacHagoel
Copy link

+1
is there any solution on the horizon?

@bluwy
Copy link
Member

bluwy commented Oct 7, 2021

@isaacHagoel The alternative is to use ResizeObserver instead of the iframe technique used. There are existing PRs: #5524 and #5963. But you can hack one quickly using actions, like:

const ro = new ResizeObserver((entries) => {
	for (const entry of entries) {
		entry.target.dispatchEvent(new CustomEvent("elResize"));
	}
});

export function resize(node: HTMLElement) {
	ro.observe(node);
	return {
		destroy() {
			ro.unobserve(node);
		},
	};
}
<div use:resize on:elResize={(e) => foo = e.target.offsetWidth} />

@isaacHagoel
Copy link

@bluwy great idea! thanks

@bluwy
Copy link
Member

bluwy commented Oct 8, 2021

FWIW, I've created a package to automatically use ResizeObservers and preserve the bind:clientWidth syntax: https://github.com/bluwy/svelte-fast-dimension

@isaacHagoel
Copy link

@bluwy you were able to preserve the bind: syntax?
what kind of black magic is that?
I will definitely have a look

@bluwy
Copy link
Member

bluwy commented Oct 8, 2021

@isaacHagoel anything is possible with preprocessors 😉 (PS: I'll opt-out of this conversation for a bit since it's a bit off topic, feel free to open a discussion in the repo though)

@oneezy
Copy link

oneezy commented Mar 29, 2022

I ran into this issue today while building a <Header /> component that swaps out Tailwind position classes (absolute | fixed) depending on the windows scrollY position and header's clientHeight.

<header bind:clientHeight={header}>...</header>

I was super confused why my classes weren't applying and thought I had a bug somewhere..but I eventually opened up dev tools to find the inline ghost style position: relative. The simplest hack fix for my scenario was adding the position classes to the component styles.

<style>
  .absolute { position: absolute; }
  .fixed { position: fixed; }
</style>

Would be cool to see a fix for this in the future!

@searleser97
Copy link

Agree that position: relative should definitely not be added, this is not what should be expected from the developer perspective. Are there plans to remove this weird and not desired behavior?

@ddanielou
Copy link

ddanielou commented Jan 25, 2023

FWIW, this also happens to me intermittently on Chrome.

Can't reproduce 100% reliably, but position: relative stays on when resizing the window from "large" to "small" such that it toggles my "small" and "large" breakpoints. It doesn't stay on when resizing from "small" to "large". It also doesn't stay on when resizing "small" → "large" → back to "small". I have a position: fixed rule at "small" sizes on the element that's giving me trouble.

@sandersrd33
Copy link

This is still an issue. Took me an entire day to figure out why a list of components I was creating was getting position: relative added magically and messing up the position:absolute element I was trying to get styled and positioned correctly on top of them.
I don't think a javascript framework should be adjusting styles on the fly like this. This was really difficult to debug because there is no indication whatsoever in any documentation that if I add bind:clientHeight to a div I'm also getting position:relative as well.

@fajfaj1
Copy link

fajfaj1 commented Nov 10, 2023

I've faced this issue in Vivaldi (chromium) browser, it appears 100% times.

@dummdidumm
Copy link
Member

Svelte 5 uses a resize observer instead, which should solve this

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

No branches or pull requests