Search code examples
javascriptmobile-safarisvelte-3

Svelte window element on:load callback not firing in mobile Safari


I am working on a Svelte and Sapper static site which I am exporting and uploading to a server. The rollup.config.js and package.json are as in the sapper-template on Github with a slight modification to the package.json to avoid issues with .DS_store when developing on mac:

"scripts": {
  "dev": "yarn dsstore:delete && sapper dev",
  "build": "yarn dsstore:delete && sapper build --legacy",
  "export": "yarn dsstore:delete && sapper export --legacy",
  "start": "node __sapper__/build",
  "dsstore:delete": "find . -name \".DS_Store\" -delete"
}

I am using the following code in a svelte component that is repeated about ten times on the home page. This is a site where high-quality photography is the main content, so I am loading a heavy image as a background-image of the component. To smooth the image loading experience, I am first loading a very light placeholder image in the background, and then, based on the window size, loading one of two different sized background-images on top:

<script>
    export let project;
    let { id, src, src_small } = project;

    let el;
    let breakpointCondition = "(max-width: 1000px)";
    let view;
    let imageSrc;

    const checkView = (e) => {
        console.log(document);
        el = document.getElementById(id);
        console.log(el);
        view = window.matchMedia(breakpointCondition);
        if (view.matches) {
            imageSrc = src_small;
        } else {
            imageSrc = src;
        }

        el.style.backgroundImage = `url(${imageSrc}), url('images/placeholder.png')`;   
    }

</script>

<svelte:window on:load={checkView} on:resize={checkView} />

<section id={id} >

...

<section>

<style>

    section {
        background-image: url('/images/placeholder.png');
    }

</style>

This code works as expected on desktop browsers. Once the page loads, the svelte window element becomes available and I can query it in order to load the appropriate background image. On ios browsers (mobile Safari, Chrome, and Firefox), the background images from the javascript do not load when I navigate to the page, though if I refresh the page, they do.

I tried substituting:

<svelte:window on:load={console.log('load')} on:resize={console.log('resize')} />

and found that the window.onload and window.onresize events are firing on both desktop and mobile browsers on page load. Strangely, no logs appear when I resize the window after the page loads, but the window.matchMedia method appears to continue doing its job and the image swap happens at the appropriate breakpoint.

When I place logs in the beginning of the checkView callback function, I see that while the onload event is firing, the function is not firing when the page first loads on ios mobile browsers. When I reload the page on ios browsers, the callback function fires and I get the expected results.

Why might this be happening and how might I work around it? This method works well on desktop.

UPDATE: Using onMount to fire checkView when the page loads and

<svelte:window on:resize={checkView} />

when the window is resized works. Question is still open in case anyone has an answer for why

<svelte:window on:load={checkView}>

won't fire the callback on initial page load in mobile Safari.


Solution

  • As far as I know, <svelte:window /> lets you listen to window events, so you can use it, for instance, by on:resize to listen to the 'resize' events. As for on:load:

    (see MDN's Window: load event:)

    The load event is fired when the whole page has loaded, including all dependent resources such as stylesheets and images. This is in contrast to DOMContentLoaded, which is fired as soon as the page DOM has been loaded, without waiting for resources to finish loading.

    So, if the page is loaded and the load event has already been fired, listening to this event is redundant and the callback will never call. Check in your code when the page is really done loading (index.html?)

    If you want to be sure the DOM elements in a specific Svelte component are done rendering, Svelte suggests their onMount(() => {}) callback:

    <script>
        export let project;
        let { id, src, src_small } = project;
    
        let el;
        let breakpointCondition = "(max-width: 1000px)";
        let view;
        let imageSrc;
    
        onMount(() => {
            console.log(document);
            el = document.getElementById(id);
            console.log(el);
            view = window.matchMedia(breakpointCondition);
            if (view.matches) {
                imageSrc = src_small;
            } else {
                imageSrc = src;
            }
    
            el.style.backgroundImage = `url(${imageSrc}), url('images/placeholder.png')`;   
        })
    
    </script>
    
    <section id={id} >
    ...
    </section>
    
    <style>
    
        section {
            background-image: url('/images/placeholder.png');
        }
    
    </style>