diff options
Diffstat (limited to 'files/uk/web/api/intersection_observer_api/index.html')
-rw-r--r-- | files/uk/web/api/intersection_observer_api/index.html | 610 |
1 files changed, 610 insertions, 0 deletions
diff --git a/files/uk/web/api/intersection_observer_api/index.html b/files/uk/web/api/intersection_observer_api/index.html new file mode 100644 index 0000000000..49a9ce1d06 --- /dev/null +++ b/files/uk/web/api/intersection_observer_api/index.html @@ -0,0 +1,610 @@ +--- +title: Intersection Observer API +slug: Web/API/Intersection_Observer_API +tags: + - API + - Clipping + - Intersection + - Intersection Observer API + - IntersectionObserver + - Overview + - Performance + - Reference + - Web + - Швидкодія +translation_of: Web/API/Intersection_Observer_API +--- +<div>{{DefaultAPISidebar("Intersection Observer API")}}</div> + +<p class="summary"><strong>Intersection Observer API</strong> дозволяє веб-застосункам асинхронно стежити за зміною перетину елемента з його батьком чи областю видимості документа <span class="seoSummary">{{Glossary("viewport")}}</span>.</p> + +<p>Історично склалося так, що виявлення видимості окремого елемента чи видимості двох елементів по відношенню один до одного було непростою задачею. Варіанти вирішення цієї задачі були ненадійними і сповільнювали роботу браузера. На жаль, з "дорослішанням" вебу, необхідність у вирішенні цієї проблеми тільки зростала, з різних причин, таких як:</p> + +<ul> + <li>Відкладене завантаження зображень чи іншого контенту по мірі прокрутки сторінки.</li> + <li>Реалізація веб-сайтів з "безкінечним скролом", де контент підвантажується по мірі того як сторінка прокручується вниз, і користувачу не потрібно переключатися між сторінками.</li> + <li>Звіт про видимість реклами з метою порахувати доходи від неї.</li> + <li>Прийняття рішення, запускати чи ні якийсь процес чи анімацію в залежності від того, побачить користувач результат чи ні.</li> +</ul> + +<p>Раніше реалізація виявлення пересічення елементів означала використання обробників подій і циклів, викликаючих методи типу {{domxref("Element.getBoundingClientRect()")}}, щоб зібрати необхідну інформацію про кожен зачеплений елемент. Оскільки весь цей код працює в основному потоці, виникають проблеми з продуктивністю.</p> + +<p>Розглянемо веб-сторінку з безкінечним скролом. На сторінці використовується бібліотека для керування періодично розміщуваною по всій сторінці рекламою, всюди анімована графіка, а також бібліотека для відображення спливаючих вікон. І всі ці речі використовують свої власні правила для визначення перетинів, і всі вони запущені в основному потоці. Автор сайту може навіть не підозрювати про цю проблему, а також може не знати, як працюють сторонні бібліотеки зсередини. В той же час користувач по ходу прокрутки сторінки стикається з тим, що робота сайта сповільнюється постійним спрацьовуванням виявлення перетинів, що в підсумку призводить до того, що користувач не задоволений браузером, сайтом і своїм комп'ютером.</p> + +<p><strong>Intersection Observer API</strong> дає можливість зареєструвати callback-функцію, яка виконається при перетині спостережуваним елементом кордонів іншого елемента (чи області видимості документа {{Glossary("viewport")}}), або при зміні величини перетину на визначене значення. Таким чином, більше немає необхідності вираховувати перетин елементів в основному потоці, і браузер може оптимізовувати ці процеси на свій погляд.</p> + +<p>Observer API не дозволить дізнатися точне число пікселів чи визначати конкретні пікселі в перетині; проте, його використання покриває найбільш поширені випадки, наприклад <em>"Якщо елементи перетинаються на N%, зроби це і це"</em>.</p> + +<h2 id="Основні_поняття_і_використання_Intersection_Observer_API">Основні поняття і використання <strong>Intersection Observer API </strong></h2> + +<p>Intersection Observer API дозволяє вказати функцію, яка буде викликана щоразу для елемента (target) при перетині його з областю видимості документа (по-замовчуванню) чи заданим елементом (root).</p> + +<p>Зазвичай, використовується відстеження перетину елементу з областю видимості (необхідно вказати <code>null</code> в якості кореневого елементу).</p> + +<p>Чи використовуємо ми область видимості чи інший елемент в якості кореневого, API працює однаково, викликаючи задану вами функцію зворотнього виклику, щоразу, коли видимість цільового елементу змінюється так, що вона перетинає в потрібному ступені кореневий елемент.</p> + +<p>Ступінь перетину цільового і кореневого елементу задається в діапазоні від 0.0 до 1.0, де 1.0 це повний перетин цільовим елементом кордонів кореневого.</p> + +<h3 id="Створення_intersection_observer">Створення intersection observer</h3> + +<p>Для початку з допомогою конструктору потрібно створити об'єкт-спостерігач, вказати для нього функцію для виклику і налаштування відстеження:</p> + +<pre class="brush: js notranslate">let options = { + root: document.querySelector('#scrollArea'), + rootMargin: '0px', + threshold: 1.0 +} + +let observer = new IntersectionObserver(callback, options); +</pre> + +<p>Параметр <strong>threshold</strong> зі значенням <strong>1.0</strong> означає що функція буде викликана при 100% перетину об'єкта (за яким ми стежимо) з об'єктом root.</p> + +<h4 id="Налаштування_Intersection_observer">Налаштування Intersection observer</h4> + +<p>The <code>options</code> object passed into the {{domxref("IntersectionObserver.IntersectionObserver", "IntersectionObserver()")}} constructor let you control the circumstances under which the observer's callback is invoked. It has the following fields:</p> + +<dl> + <dt><code>root</code></dt> + <dd>Елемент який використовується в якості області перегляду для перевірки видимості цільового елементу. Повинен бути батьком цільового елементу. По замовчуванню використовується область видимості браузера якщо не визначений або має значення <code>null</code>.</dd> + <dt><code>rootMargin</code></dt> + <dd> + <p>Відступи навколо root. Можуть мати значення як властивість css margin: <code>10px 20px 30px 40px"</code> (top, right, bottom, left). Значення можна задавати у відсотках. По замовчуванню всі параметри встановлені в нулі.</p> + </dd> + <dt><code>threshold</code></dt> + <dd>Число чи масив чисел, який вказує, при якому відсотку видимості цільового елементу повинен спрацювати callback. Наприклад, в цьому випадку callback функція буде викликатися при появі в зоні видимості кожні 25% цільового елементу: [0, 0.25, 0.5, 0.75, 1]. Дефолтне значення 0 (як тільки хоча б один піксель стане видимим, то буде викликаний колбек). Значення 1.0 означає, що увесь елемент повністю має стати видимим перед тим, як буде викликано колбек.</dd> +</dl> + +<h4 id="Цільовий_елемент_який_буде_спостерігатися">Цільовий елемент, який буде спостерігатися</h4> + +<p>Після того, як ви створили спостерігача, вам потрібно дати йому цільовий елемент для перегляду:</p> + +<pre class="brush: js notranslate">let target = document.querySelector('#listItem'); +observer.observe(target); + +// the callback we setup for the observer will be executed now for the first time +// it waits until we assign a target to our observer (even if the target is currently not visible) +</pre> + +<p>Щоразу, коли ціль досягає порогового значення, вказаного для IntersectionObserver, викликається функція зворотнього виклику callback. Де callback отримує список об'єктів IntersectionObserverEntry і спостерігача:</p> + +<pre class="brush: js notranslate">let callback = (entries, observer) => { + entries.forEach(entry => { + // Each entry describes an intersection change for one observed + // target element: + // entry.boundingClientRect + // entry.intersectionRatio + // entry.intersectionRect + // entry.isIntersecting + // entry.rootBounds + // entry.target + // entry.time + }); +}; +</pre> + +<p>Зверніть увагу, що функція зворотнього виклику запускається в головному потоці і повинна виконуватися якнайшвидше, тому якщо щось забирає багато часу, то використовуйте {{domxref("Window.requestIdleCallback()")}}.</p> + +<p>Також зверніть увагу, що якщо ви вказали опцію <code>root</code>, цільовий елемент повинен бути потомком кореневого елементу. </p> + +<h3 id="How_intersection_is_calculated">How intersection is calculated</h3> + +<p>All areas considered by the Intersection Observer API are rectangles; elements which are irregularly shaped are considered as occupying the smallest rectangle which encloses all of the element's parts. Similarly, if the visible portion of an element is not rectangular, the element's intersection rectangle is construed to be the smallest rectangle that contains all the visible portions of the element.</p> + +<p>It's useful to understand a bit about how the various properties provided by {{domxref("IntersectionObserverEntry")}} describe an intersection.</p> + +<h4 id="The_intersection_root_and_root_margin">The intersection root and root margin</h4> + +<p>Before we can track the intersection of an element with a container, we need to know what that container is. That container is the <strong>intersection root</strong>, or <strong>root element</strong>. This can be either a specific element in the document which is an ancestor of the element to be observed, or <code>null</code> to use the document's viewport as the container.</p> + +<p>The <strong><dfn>root intersection rectangle</dfn></strong> is the rectangle used to check against the target or targets. This rectangle is determined like this:</p> + +<ul> + <li>If the intersection root is the implicit root (that is, the top-level {{domxref("Document")}}), the root intersection rectangle is the viewport's rectangle.</li> + <li>If the intersection root has an overflow clip, the root intersection rectangle is the root element's content area.</li> + <li>Otherwise, the root intersection rectangle is the intersection root's bounding client rectangle (as returned by calling {{domxref("Element.getBoundingClientRect", "getBoundingClientRect()")}} on it).</li> +</ul> + +<p>The root intersection rectangle can be adjusted further by setting the <strong>root margin</strong>, <code>rootMargin</code>, when creating the {{domxref("IntersectionObserver")}}. The values in <code>rootMargin</code> define offsets added to each side of the intersection root's bounding box to create the final intersection root bounds (which are disclosed in {{domxref("IntersectionObserverEntry.rootBounds")}} when the callback is executed).</p> + +<h4 id="Thresholds">Thresholds</h4> + +<p>Rather than reporting every infinitesimal change in how much a target element is visible, the Intersection Observer API uses <strong>thresholds</strong>. When you create an observer, you can provide one or more numeric values representing percentages of the target element which are visible. Then, the API only reports changes to visibility which cross these thresholds.</p> + +<p>For example, if you want to be informed every time a target's visibility passes backward or forward through each 25% mark, you would specify the array [0, 0.25, 0.5, 0.75, 1] as the list of thresholds when creating the observer. You can tell which direction the visibility changed in (that is, whether the element became more visible or less visible) by checking the value of the {{domxref("IntersectionObserverEntry.isIntersecting", "isIntersecting")}} property on the {{domxref("IntersectionObserverEntry")}} passed into the callback function at the time of the visibility change. If <code>isIntersecting</code> is <code>true</code>, the target element has become at least as visible as the threshold that was passed. If it's <code>false</code>, the target is no longer as visible as the given threshold.</p> + +<p>To get a feeling for how thresholds work, try scrolling the box below around. Each colored box within it displays the percentage of itself that's visible in all four of its corners, so you can see these ratios change over time as you scroll the container. Each box has a different set of thresholds:</p> + +<ul> + <li>The first box has a threshold for each percentage point of visibility; that is, the {{domxref("IntersectionObserver.thresholds")}} array is <code>[0.00, 0.01, 0.02, ..., 0.99, 1.00]</code>.</li> + <li>The second box has a single threshold, at the 50% mark.</li> + <li>The third box has thresholds every 10% of visibility (0%, 10%, 20%, etc.).</li> + <li>The last box has thresholds each 25%.</li> +</ul> + +<div class="hidden"> +<h5 id="Threshold_example">Threshold example</h5> + +<pre class="brush: html notranslate"><template id="boxTemplate"> + <div class="sampleBox"> + <div class="label topLeft"></div> + <div class="label topRight"></div> + <div class="label bottomLeft"></div> + <div class="label bottomRight"></div> + </div> +</template> + +<main> + <div class="contents"> + <div class="wrapper"> + </div> + </div> +</main></pre> + +<pre class="brush: css notranslate">.contents { + position: absolute; + width: 700px; + height: 1725px; +} + +.wrapper { + position: relative; + top: 600px; +} + +.sampleBox { + position: relative; + left: 175px; + width: 150px; + background-color: rgb(245, 170, 140); + border: 2px solid rgb(201, 126, 17); + padding: 4px; + margin-bottom: 6px; +} + +#box1 { + height: 200px; +} + +#box2 { + height: 75px; +} + +#box3 { + height: 150px; +} + +#box4 { + height: 100px; +} + +.label { + font: 14px "Open Sans", "Arial", sans-serif; + position: absolute; + margin: 0; + background-color: rgba(255, 255, 255, 0.7); + border: 1px solid rgba(0, 0, 0, 0.7); + width: 3em; + height: 18px; + padding: 2px; + text-align: center; +} + +.topLeft { + left: 2px; + top: 2px; +} + +.topRight { + right: 2px; + top: 2px; +} + +.bottomLeft { + bottom: 2px; + left: 2px; +} + +.bottomRight { + bottom: 2px; + right: 2px; +} +</pre> + +<pre class="brush: js notranslate">let observers = []; + +startup = () => { + let wrapper = document.querySelector(".wrapper"); + + // Options for the observers + + let observerOptions = { + root: null, + rootMargin: "0px", + threshold: [] + }; + + // An array of threshold sets for each of the boxes. The + // first box's thresholds are set programmatically + // since there will be so many of them (for each percentage + // point). + + let thresholdSets = [ + [], + [0.5], + [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], + [0, 0.25, 0.5, 0.75, 1.0] + ]; + + for (let i=0; i<=1.0; i+= 0.01) { + thresholdSets[0].push(i); + } + + // Add each box, creating a new observer for each + + for (let i=0; i<4; i++) { + let template = document.querySelector("#boxTemplate").content.cloneNode(true); + let boxID = "box" + (i+1); + template.querySelector(".sampleBox").id = boxID; + wrapper.appendChild(document.importNode(template, true)); + + // Set up the observer for this box + + observerOptions.threshold = thresholdSets[i]; + observers[i] = new IntersectionObserver(intersectionCallback, observerOptions); + observers[i].observe(document.querySelector("#" + boxID)); + } + + // Scroll to the starting position + + document.scrollingElement.scrollTop = wrapper.firstElementChild.getBoundingClientRect().top + window.scrollY; + document.scrollingElement.scrollLeft = 750; +} + +intersectionCallback = (entries) => { + entries.forEach((entry) => { + let box = entry.target; + let visiblePct = (Math.floor(entry.intersectionRatio * 100)) + "%"; + + box.querySelector(".topLeft").innerHTML = visiblePct; + box.querySelector(".topRight").innerHTML = visiblePct; + box.querySelector(".bottomLeft").innerHTML = visiblePct; + box.querySelector(".bottomRight").innerHTML = visiblePct; + }); +} + +startup(); +</pre> +</div> + +<p>{{EmbedLiveSample("Threshold_example", 500, 500)}}</p> + +<h4 id="Clipping_and_the_intersection_rectangle">Clipping and the intersection rectangle</h4> + +<p>The browser computes the final intersection rectangle as follows; this is all done for you, but it can be helpful to understand these steps in order to better grasp exactly when intersections will occur.</p> + +<ol> + <li>The target element's bounding rectangle (that is, the smallest rectangle that fully encloses the bounding boxes of every component that makes up the element) is obtained by calling {{domxref("Element.getBoundingClientRect", "getBoundingClientRect()")}} on the target. This is the largest the intersection rectangle may be. The remaining steps will remove any portions that don't intersect.</li> + <li>Starting at the target's immediate parent block and moving outward, each containing block's clipping (if any) is applied to the intersection rectangle. A block's clipping is determined based on the intersection of the two blocks and the clipping mode (if any) specified by the {{cssxref("overflow")}} property. Setting <code>overflow</code> to anything but <code>visible</code> causes clipping to occur.</li> + <li>If one of the containing elements is the root of a nested browsing context (such as the document contained in an {{HTMLElement("iframe")}}, the intersection rectangle is clipped to the containing context's viewport, and recursion upward through the containers continues with the container's containing block. So if the top level of an <code><iframe></code> is reached, the intersection rectangle is clipped to the frame's viewport, then the frame's parent element is the next block recursed through toward the intersection root.</li> + <li>When recursion upward reaches the intersection root, the resulting rectangle is mapped to the intersection root's coordinate space.</li> + <li>The resulting rectangle is then updated by intersecting it with the <a href="/en-US/docs/Web/API/Intersection_Observer_API#root-intersection-rectangle">root intersection rectangle</a>.</li> + <li>This rectangle is, finally, mapped to the coordinate space of the target's {{domxref("document")}}.</li> +</ol> + +<h3 id="Intersection_change_callbacks">Intersection change callbacks</h3> + +<p>When the amount of a target element which is visible within the root element crosses one of the visibility thresholds, the {{domxref("IntersectionObserver")}} object's callback is executed. The callback receives as input an array of all of <code>IntersectionObserverEntry</code> objects, one for each threshold which was crossed, and a reference to the <code>IntersectionObserver</code> object itself.</p> + +<p>Each entry in the list of thresholds is an {{domxref("IntersectionObserverEntry")}} object describing one threshold that was crossed; that is, each entry describes how much of a given element is intersecting with the root element, whether or not the element is considered to be intersecting or not, and the direction in which the transition occurred.</p> + +<p>The code snippet below shows a callback which keeps a counter of how many times elements transition from not intersecting the root to intersecting by at least 75%.</p> + +<pre class="brush: js notranslate">intersectionCallback(entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + let elem = entry.target; + + if (entry.intersectionRatio >= 0.75) { + intersectionCounter++; + } + } + }); +} +</pre> + +<h2 id="Інтерфейси">Інтерфейси</h2> + +<dl> + <dt>{{domxref("IntersectionObserver")}}</dt> + <dd>Основний інтерфейс для API Intersection Observer. Надає методи для створення і керування observer, який може спостерігати будь-яку кількість цільових елементів для однієї і тієї ж конфігурації перетину. Кожен observer може асинхронно спостерігати зміни в перетині між одним чи кількома цільовими елементами і спільним елементом-батьком чи з їх верхнім рівнем Document's viewport. Батько чи область перегляду згадується як <strong>root</strong>.</dd> + <dt>{{domxref("IntersectionObserverEntry")}}</dt> + <dd> + <p>Описує перетин між цільовим елементом і його кореневим контейнером в певний момент переходу. Об'єкти цього типу можуть бути отримані тільки двома способами: в якості вхідних даних для вашого зворотнього виклику <code>IntersectionObserver</code> чи шляхом виклику {{domxref("IntersectionObserver.takeRecords()")}}.</p> + </dd> +</dl> + +<h2 id="A_simple_example">A simple example</h2> + +<p>This simple example causes a target element to change its color and transparency as it becomes more or less visible. At <a href="/en-US/docs/Web/API/Intersection_Observer_API/Timing_element_visibility">Timing element visibility with the Intersection Observer API</a>, you can find a more extensive example showing how to time how long a set of elements (such as ads) are visible to the user and to react to that information by recording statistics or by updating elements..</p> + +<h3 id="HTML">HTML</h3> + +<p>The HTML for this example is very short, with a primary element which is the box that we'll be targeting (with the creative ID <code>"box"</code>) and some contents within the box.</p> + +<pre class="brush: html notranslate"><div id="box"> + <div class="vertical"> + Welcome to <strong>The Box!</strong> + </div> +</div></pre> + +<h3 id="CSS">CSS</h3> + +<p>The CSS isn't terribly important for the purposes of this example; it lays out the element and establishes that the {{cssxref("background-color")}} and {{cssxref("border")}} attributes can participate in <a href="/en-US/docs/Web/CSS/CSS_Transitions">CSS transitions</a>, which we'll use to affect the changes to the element as it becomes more or less obscured.</p> + +<pre class="brush: css notranslate">#box { + background-color: rgba(40, 40, 190, 255); + border: 4px solid rgb(20, 20, 120); + transition: background-color 1s, border 1s; + width: 350px; + height: 350px; + display: flex; + align-items: center; + justify-content: center; + padding: 20px; +} + +.vertical { + color: white; + font: 32px "Arial"; +} + +.extra { + width: 350px; + height: 350px; + margin-top: 10px; + border: 4px solid rgb(20, 20, 120); + text-align: center; + padding: 20px; +}</pre> + +<h3 id="JavaScript">JavaScript</h3> + +<p>Finally, let's take a look at the JavaScript code that uses the Intersection Observer API to make things happen.</p> + +<h4 id="Setting_up">Setting up</h4> + +<p>First, we need to prepare some variables and install the observer.</p> + +<pre class="brush: js notranslate">const numSteps = 20.0; + +let boxElement; +let prevRatio = 0.0; +let increasingColor = "rgba(40, 40, 190, ratio)"; +let decreasingColor = "rgba(190, 40, 40, ratio)"; + +// Set things up +window.addEventListener("load", (event) => { + boxElement = document.querySelector("#box"); + + createObserver(); +}, false);</pre> + +<p>The constants and variables we set up here are:</p> + +<dl> + <dt><code>numSteps</code></dt> + <dd>A constant which indicates how many thresholds we want to have between a visibility ratio of 0.0 and 1.0.</dd> + <dt><code>prevRatio</code></dt> + <dd>This variable will be used to record what the visibility ratio was the last time a threshold was crossed; this will let us figure out whether the target element is becoming more or less visible.</dd> + <dt><code>increasingColor</code></dt> + <dd>A string defining a color we'll apply to the target element when the visibility ratio is increasing. The word "ratio" in this string will be replaced with the target's current visibility ratio, so that the element not only changes color but also becomes increasingly opaque as it becomes less obscured.</dd> + <dt><code>decreasingColor</code></dt> + <dd>Similarly, this is a string defining a color we'll apply when the visibility ratio is decreasing.</dd> +</dl> + +<p>We call {{domxref("EventTarget.addEventListener", "Window.addEventListener()")}} to start listening for the {{event("load")}} event; once the page has finished loading, we get a reference to the element with the ID <code>"box"</code> using {{domxref("Document.querySelector", "querySelector()")}}, then call the <code>createObserver()</code> method we'll create in a moment to handle building and installing the intersection observer.</p> + +<h4 id="Creating_the_intersection_observer">Creating the intersection observer</h4> + +<p>The <code>createObserver()</code> method is called once page load is complete to handle actually creating the new {{domxref("IntersectionObserver")}} and starting the process of observing the target element.</p> + +<pre class="brush: js notranslate">function createObserver() { + let observer; + + let options = { + root: null, + rootMargin: "0px", + threshold: buildThresholdList() + }; + + observer = new IntersectionObserver(handleIntersect, options); + observer.observe(boxElement); +}</pre> + +<p>This begins by setting up an <code>options</code> object containing the settings for the observer. We want to watch for changes in visibility of the target element relative to the document's viewport, so <code>root</code> is <code>null</code>. We need no margin, so the margin offset, <code>rootMargin</code>, is specified as "0px". This causes the observer to watch for changes in the intersection between the target element's bounds and those of the viewport, without any added (or subtracted) space.</p> + +<p>The list of visibility ratio thresholds, <code>threshold</code>, is constructed by the function <code>buildThresholdList()</code>. The threshold list is built programmatically in this example since there are a number of them and the number is intended to be adjustable.</p> + +<p>Once <code>options</code> is ready, we create the new observer, calling the {{domxref("IntersectionObserver.IntersectionObserver", "IntersectionObserver()")}} constructor, specifying a function to be called when intersection crosses one of our thresholds, <code>handleIntersect()</code>, and our set of options. We then call {{domxref("IntersectionObserver.observe", "observe()")}} on the returned observer, passing into it the desired target element.</p> + +<p>We could opt to monitor multiple elements for visibility intersection changes with respect to the viewport by calling <code>observer.observe()</code> for each of those elements, if we wanted to do so.</p> + +<h4 id="Building_the_array_of_threshold_ratios">Building the array of threshold ratios</h4> + +<p>The <code>buildThresholdList()</code> function, which builds the list of thresholds, looks like this:</p> + +<pre class="brush: js notranslate">function buildThresholdList() { + let thresholds = []; + let numSteps = 20; + + for (let i=1.0; i<=numSteps; i++) { + let ratio = i/numSteps; + thresholds.push(ratio); + } + + thresholds.push(0); + return thresholds; +}</pre> + +<p>This builds the array of thresholds—each of which is a ratio between 0.0 and 1.0, by pushing the value <code>i/numSteps</code> onto the <code>thresholds</code> array for each integer <code>i</code> between 1 and <code>numSteps</code>. It also pushes 0 to include that value. The result, given the default value of <code>numSteps</code> (20), is the following list of thresholds:</p> + +<table class="standard-table"> + <tbody> + <tr> + <th>#</th> + <th>Ratio</th> + <th>#</th> + <th>Ratio</th> + </tr> + <tr> + <th>1</th> + <td>0.05</td> + <th>11</th> + <td>0.55</td> + </tr> + <tr> + <th>2</th> + <td>0.1</td> + <th>12</th> + <td>0.6</td> + </tr> + <tr> + <th>3</th> + <td>0.15</td> + <th>13</th> + <td>0.65</td> + </tr> + <tr> + <th>4</th> + <td>0.2</td> + <th>14</th> + <td>0.7</td> + </tr> + <tr> + <th>5</th> + <td>0.25</td> + <th>15</th> + <td>0.75</td> + </tr> + <tr> + <th>6</th> + <td>0.3</td> + <th>16</th> + <td>0.8</td> + </tr> + <tr> + <th>7</th> + <td>0.35</td> + <th>17</th> + <td>0.85</td> + </tr> + <tr> + <th>8</th> + <td>0.4</td> + <th>18</th> + <td>0.9</td> + </tr> + <tr> + <th>9</th> + <td>0.45</td> + <th>19</th> + <td>0.95</td> + </tr> + <tr> + <th>10</th> + <td>0.5</td> + <th>20</th> + <td>1.0</td> + </tr> + </tbody> +</table> + +<p>We could, of course, hard-code the array of thresholds into our code, and often that's what you'll end up doing. But this example leaves room for adding configuration controls to adjust the granularity, for example.</p> + +<h4 id="Handling_intersection_changes">Handling intersection changes</h4> + +<p>When the browser detects that the target element (in our case, the one with the ID <code>"box"</code>) has been unveiled or obscured such that its visibility ratio crosses one of the thresholds in our list, it calls our handler function, <code>handleIntersect()</code>:</p> + +<pre class="brush: js notranslate">function handleIntersect(entries, observer) { + entries.forEach((entry) => { + if (entry.intersectionRatio > prevRatio) { + entry.target.style.backgroundColor = increasingColor.replace("ratio", entry.intersectionRatio); + } else { + entry.target.style.backgroundColor = decreasingColor.replace("ratio", entry.intersectionRatio); + } + + prevRatio = entry.intersectionRatio; + }); +}</pre> + +<p>For each {{domxref("IntersectionObserverEntry")}} in the list <code>entries</code>, we look to see if the entry's {{domxref("IntersectionObserverEntry.intersectionRatio", "intersectionRatio")}} is going up; if it is, we set the target's {{cssxref("background-color")}} to the string in <code>increasingColor</code> (remember, it's <code>"rgba(40, 40, 190, ratio)"</code>), replaces the word "ratio" with the entry's <code>intersectionRatio</code>. The result: not only does the color get changed, but the transparency of the target element changes, too; as the intersection ratio goes down, the background color's alpha value goes down with it, resulting in an element that's more transparent.</p> + +<p>Similarly, if the <code>intersectionRatio</code> is going down, we use the string <code>decreasingColor</code> and replace the word "ratio" in that with the <code>intersectionRatio</code> before setting the target element's <code>background-color</code>.</p> + +<p>Finally, in order to track whether the intersection ratio is going up or down, we remember the current ratio in the variable <code>prevRatio</code>.</p> + +<h3 id="Result">Result</h3> + +<p>Below is the resulting content. Scroll this page up and down and notice how the appearance of the box changes as you do so.</p> + +<p>{{EmbedLiveSample('A_simple_example', 400, 400)}}</p> + +<p>There's an even more extensive example at <a href="/en-US/docs/Web/API/Intersection_Observer_API/Timing_element_visibility">Timing element visibility with the Intersection Observer API</a>.</p> + +<h2 id="Specifications">Specifications</h2> + +<table class="standard-table"> + <thead> + <tr> + <th scope="col">Specification</th> + <th scope="col">Status</th> + <th scope="col">Comment</th> + </tr> + </thead> + <tbody> + <tr> + <td>{{SpecName('IntersectionObserver')}}</td> + <td>{{Spec2('IntersectionObserver')}}</td> + <td></td> + </tr> + </tbody> +</table> + +<h2 id="Browser_compatibility">Browser compatibility</h2> + + + +<p>{{Compat("api.IntersectionObserver")}}</p> + +<h2 id="See_also">See also</h2> + +<ul> + <li><a href="https://github.com/w3c/IntersectionObserver">Intersection Observer polyfill</a></li> + <li><a href="/en-US/docs/Web/API/Intersection_Observer_API/Timing_element_visibility">Timing element visibility with the Intersection Observer API</a></li> + <li>{{domxref("IntersectionObserver")}} and {{domxref("IntersectionObserverEntry")}}</li> +</ul> + +<div id="gtx-trans" style="position: absolute; left: 22px; top: 11013px;"> +<div class="gtx-trans-icon"></div> +</div> |