Ten wzorzec pokazuje, jak utworzyć komponent Relacje na stronie internetowej, który jest elastyczny, obsługuje nawigację za pomocą klawiatury i działa w różnych przeglądarkach.
Pełny artykuł · Film w YouTube · Źródło: GitHub
HTML
<div class="stories"> <section class="user"> <article class="story" style="--bg: url(https://picsum.photos/480/840);"></article> <article class="story" style="--bg: url(https://picsum.photos/480/841);"></article> </section> <section class="user"> <article class="story" style="--bg: url(https://picsum.photos/481/840);"></article> </section> <section class="user"> <article class="story" style="--bg: url(https://picsum.photos/481/841);"></article> </section> <section class="user"> <article class="story" style="--bg: url(https://picsum.photos/482/840);"></article> <article class="story" style="--bg: url(https://picsum.photos/482/843);"></article> <article class="story" style="--bg: url(https://picsum.photos/482/844);"></article> </section> </div>
CSS
.stories { display: grid; grid: 1fr / auto-flow 100%; grid-gap: 1ch; gap: 1ch; overflow-x: auto; scroll-snap-type: x mandatory; overscroll-behavior: contain; touch-action: pan-x; } .user { scroll-snap-align: start; scroll-snap-stop: always; display: grid; grid: [story] 1fr / [story] 1fr; } .story { grid-area: story; background-size: cover; background-image: var(--bg), linear-gradient(to top, rgb(249, 249, 249), rgb(226, 226, 226)); user-select: none; touch-action: manipulation; transition: opacity .3s cubic-bezier(0.4, 0.0, 1, 1) } .story.seen { opacity: 0; pointer-events: none; }
JS
const stories = document.querySelector('.stories') const median = stories.offsetLeft + (stories.clientWidth / 2) const state = { current_story: stories.firstElementChild.lastElementChild } const navigateStories = direction => { const story = state.current_story const lastItemInUserStory = story.parentNode.firstElementChild const firstItemInUserStory = story.parentNode.lastElementChild const hasNextUserStory = story.parentElement.nextElementSibling const hasPrevUserStory = story.parentElement.previousElementSibling if (direction === 'next') { if (lastItemInUserStory === story && !hasNextUserStory) return else if (lastItemInUserStory === story && hasNextUserStory) { state.current_story = story.parentElement.nextElementSibling.lastElementChild story.parentElement.nextElementSibling.scrollIntoView({ behavior: 'smooth' }) } else { story.classList.add('seen') state.current_story = story.previousElementSibling } } else if(direction === 'prev') { if (firstItemInUserStory === story && !hasPrevUserStory) return else if (firstItemInUserStory === story && hasPrevUserStory) { state.current_story = story.parentElement.previousElementSibling.firstElementChild story.parentElement.previousElementSibling.scrollIntoView({ behavior: 'smooth' }) } else { story.nextElementSibling.classList.remove('seen') state.current_story = story.nextElementSibling } } } stories.addEventListener('click', e => { if (e.target.nodeName !== 'ARTICLE') return navigateStories( e.clientX > median ? 'next' : 'prev') }) // left & right are free with snap points 👍 document.addEventListener('keydown', ({key}) => { if (key !== 'ArrowDown' || key !== 'ArrowUp') navigateStories( key === 'ArrowDown' ? 'next' : 'prev') })