0

I built a sort of timeline with a grid design and horizontal scrolling.

Dates are in the center row, while a picture is in 1st and some legend in 3rd.

The aim is to display pictures and legends only when their date column stands in the middle of the viewport – using javascript to change the elements’ opacity.

It’s probably not the best approach – but being a newbie and an amateur, I was happy and proud anyway that the code I wrote worked as intended. Well, not exactly…

It works fine on Windows and Android.

But not on iOS: the elements that should become “invisible” after a scroll remain visible on screen, despite having their opacity property set to zero (at least that’s what I see in the Safari inspector window).

The problem seems to be linked to the transitions applied on the opacity property.

If I suppress those transitions, the elements appear/disappear as anticipated even in iOS – just not with the desired fading in / out effect.

After hours of browsing stackoverflow and other forums – I couldn’t find any relevant post on that issue. The closest answers were about hovering and animations and 3Dpreserve, and most were a decade old, inviting to use --webkit prefix. One, however, suggested to apply “backface-visibility: hidden;” on the dynamic elements – but with no more explanation.

As a last resort, I decided to try to apply that solution to my case and – ta da ! It fixed the problem, and now my elements fade in / out nicely in my grid on iOS.

But I can’t help asking myself: wtf? Why would backface-visibility change anything in a code that does no flips and just changes a div opacity? Does anybody know about that issue? Or why would iOS behave like that with transitions?

My problem is fixed, but I’d love to understand why…..

To make things worse, I wasn’t able to replicate the problem in a simplified code: each time I build a simple grid with that same js event listener to display content when a cell reach the viewport’s middle – everything works fine on windows/android/iOS.

So here’s the full thing : github pages

I suspect it has something to do with an error in my event listener:

const timelineWrapper = document.querySelector('.timeline');

const tiles = document.querySelectorAll('button.tl_year');

timelineWrapper.addEventListener('scroll', () => { 
    const tileWidth = tiles[0].offsetWidth;    

   tiles.forEach(tile => {
        const index = Array.from(tiles).indexOf(tile);
        const posTile = (index) * tileWidth;
        const endTile = posTile + tileWidth;
        const imgSelect = document.querySelector('img[data-year="' + tile.id + '"]');
        const legendSelect = document.querySelector('.tl_legende[data-year="' + tile.id + '"]');
        const credSelect = document.querySelector('.cred[data-cred="' + tile.id + '"]');
        if((timelineWrapper.scrollLeft >= posTile - 10) && (timelineWrapper.scrollLeft < endTile - 10)) {
            tile.classList.add('date_active');
            imgSelect.classList.add('tl_LegImgVisible');
            imgSelect.setAttribute('aria-hidden', false);
            legendSelect.classList.add('tl_LegImgVisible');
            legendSelect.removeAttribute('inert');
            credSelect.classList.add('credShow');            
        }
        else {
            tile.classList.remove('date_active');
            imgSelect.classList.remove('tl_LegImgVisible');
            imgSelect.setAttribute('aria-hidden', true);
            legendSelect.classList.remove('tl_LegImgVisible');
            legendSelect.setAttribute('inert', true); 
            credSelect.classList.remove('credShow');  
        }
    })
});

But maybe it’s something else – I don’t know: I’m lost but eager to find out and learn what I did wrong… Thanks in advance for your help!

EDIT Here is a sample of the HTML :

<div class="tl_wrapper leftHidden" role="none">
    <div class="timeline" role="region" aria-roledescription="slideshow" aria-labelledby="tl_titre">
        
        
        <div class="tl_year blank" id="firstTile" aria-hidden="true"></div>
        <div class="tl_year blank" aria-hidden="true"></div>
        
        <button class="tl_year date_active" id="1951" data-year="1951" onclick="clicDate(this)" aria-label="Cliquer pour aller vers cette date">1951</button>
        <div class="tl_legende" aria-hidden="true"></div>
        <div class="tl_legende" aria-hidden="true"></div>
        <button class="tl_legende tl_LegImgVisible" data-year="1951" popovertarget="pop_1951">
            <h4>Création de la CECA</h4>
            <svg class="tl_moreInfo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
                <!-- some path -->
            </svg>
        </button>
        <!-- x the number of date and legend buttons in the grid -->

        <img aria-hidden="false" src="pix/1951.webp" alt="Dernière pages du traité de Paris, avec les signatures et sceaux apposés" data-year="1951" loading="lazy" class="tl_LegImgVisible">
        <!-- x the number of images in the grid -->

        <div class="credits_photo" aria-hidden="true">
            <div class="cred credShow" data-cred="1951">© M. Baronnet / <a href="https://creativecommons.org/licenses/by-sa/4.0/" target="_blank">CC BY-SA 4.0</a></div>
            <!-- x the number of photo credits to be displayed -->
        </div>
</div>

And a sample of the CSS for the elements with changing opacity:

#partChrono img {
    height: 90%;
    width: auto;
    grid-row: 1 / 2;
    place-self: center; 
    opacity: 0;
    transition: opacity 1s ease-in-out;
    /* backface-visibility: hidden; */
}

.tl_legende {
    width: 100%;
    grid-row: 3 / 4;
    padding-top: 5vmin;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-wrap: nowrap;
    opacity: 0;
    /* backface-visibility: hidden; */
    transition: opacity 1s ease-in-out;    
}

.tl_LegImgVisible, #partChrono img.tl_LegImgVisible {
    opacity: 1;
}

.credits_photo {
    display: flex;
    align-self: flex-end;

}

.cred {
    font-size: var(--fs--2);
    font-style: italic;
    padding: 1vmin;
    display: none
}

.credShow {
    display: inline;
}

1 Answer 1

1

We'll forget about the right way to do thing and just fix your code.

Firstly, why all that?

tile.classList.add('date_active');
imgSelect.classList.add('tl_LegImgVisible');
imgSelect.setAttribute('aria-hidden', false);
legendSelect.classList.add('tl_LegImgVisible');
legendSelect.removeAttribute('inert');
credSelect.classList.add('credShow'); 

And not something like that ?

// assume item is a wrapper or each timeline item
item.setAttribute('aria-hidden', false);

And secondary, you issue is related to opacity so where is the CSS code because I don't see where you update it and assume that it is in your CSS code.

Edit: Your code seam functionnal to me. Focusing on your issue all I can said to you is to add a fallback option using visibility: hidden/visible with a delay to not break your transitions.

#partChrono img {
    height: 90%;
    width: auto;
    grid-row: 1 / 2;
    place-self: center; 
    opacity: 0;
    visibility: hidden;
    transition:
        opacity 1s ease-in-out,
        visibility 0s linear 1s; /* set delay equal to duration */
    /* backface-visibility: hidden; */
}

.tl_legende {
    width: 100%;
    grid-row: 3 / 4;
    padding-top: 5vmin;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-wrap: nowrap;
    opacity: 0;
    visibility: hidden;
    transition:
        opacity 1s ease-in-out,
        visibility 0s linear 1s; /* set delay equal to duration */
    /* backface-visibility: hidden; */
    /* backface-visibility: hidden; */  
}

.tl_LegImgVisible, #partChrono img.tl_LegImgVisible {
    opacity: 1;
    visibility: visible;
    transition:
        opacity 1s ease-in-out,
        visibility 0s linear 0s;
}

Sorry I don't have IOS to test a solid solution for you.

Sign up to request clarification or add additional context in comments.

3 Comments

On your first point: that's what I said about not being sure of the best way to make the imagined design... I did it that way because I felt I needed to be able to control each picture and legend individually - but I may be wrong... Regarding your second point: I have a ton of css and was afraid it would be too long for my already very long post - hence the link toward the full web page. But you're right: i'll update my question and add at least the css for the div with changing opacity. Thx!
I've edited my answer. It doesn't truly solve the opacity issue but that's the best solution I can give you right now
Thank you for giving it a try! Yet unfortunately the mystery remains as to why my divs and imgs wouldn't disappear in the first place in iOS... I'll re-try ASAP to reproduce the error with a simplify code in case it can help solving this mystery . Cheers:)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.