1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
|
---
title: Intersection Observer API
slug: Web/API/Intersection_Observer_API
tags:
- API
- IntersectionObserver
- Reference
- Web
translation_of: Web/API/Intersection_Observer_API
---
<div>{{SeeCompatTable}}{{DefaultAPISidebar("Intersection Observer API")}}</div>
<p>L'API <em>Intersection Observer</em> permet d'observer de manière asynchrone l'évolution de l'intersection d'un élément cible avec un élément ancêtre ou avec la zone d'affichage d'un document de niveau supérieur.</p>
<p>Historiquement, détecter la visibilité d'un élément, ou la visibilité relative de deux éléments l'un par rapport à l'autre, a été une tache difficile, résolue de manière peu rigoureuse et pouvant nuire à la fluidité de la page consultée. Malheureusement, avec la maturation de la toile, ce genre d'information se révèle de plus en plus nécessaire. La donnée de l'intersection est requise pour de nombreuses raisons, telles que:</p>
<ul>
<li>Le chargement paresseux d'images ou d'autres types de contenus au fur et à mesure que la page défile.</li>
<li>L'implantation de « défilement infini », où de plus en plus de contenu est chargé tandis que l'utilisateur défile, afin qu'il n'ait pas à changer de page.</li>
<li>Le signalement de la visibilité pour les publicités afin de calculer les revenus publicitaires.</li>
<li>La décision d'exécuter ou non une tâche ou une animation selon que l'utilisateur va en voir le résultat ou non.</li>
</ul>
<p>De par le passé, l'implantation de la détection d'intersection impliquait des gestionnaires d'évènements et des boucles appelant des méthodes telles que {{domxref("Element.getBoundingClientRect()")}} afin de générer les informations nécessaires pour chaque élément concerné. Comme la totalité du code est exécuté dans le <em>thread</em> principal, même une seule de ces boucles peut causer des problèmes de performance. Si un site est rempli de ces tests, les choses peuvent vite devenir très moches.</p>
<p>Prenons une page qui utilise un défilement infini. Mettons qu'elle utilise une bibliothèque fournie par un éditeur afin de gérer les publicités placées périodiquement le long de la page, qu'elle a des graphiques animés ici et là, et qu'elle utilise une bibliothèque personnalisée pour dessiner des cases de notifications et ce genre de choses. Chacune de ces choses a ses propres procédures de détection d'intersection, toutes exécutées dans le <em>thread</em> principal. L'auteur du site ne le réalise peut-être même pas, puisqu'il utilise deux bibliothèques dont il ne connaît pas forcément les détails de fonctionnement. Quand l'utilisateur navigue sur la page, ces procédures de détection d'intersection réagissent constamment pendant l'exécution du code de défilement, rendant l'expérience frustrante pour l'utilisateur vis-à-vis de son navigateur, du site Internet et de son ordinateur.</p>
<p>L'API <em>Intersection Observer</em> permet d'intégrer une fonction <em>callback</em> qui est exécutée quand un élément qu'on souhaite surveiller entre ou sort d'un autre élément (ou du <em>viewport</em> (zone d'affichage)), ou quand la taille de leur intersection varie d'une quantité prédéterminée. Ainsi, les sites n'ont plus besoin de faire quoi que ce soit sur le <em>thread</em> principal pour surveiller ce genre d'intersection d'éléments, et le navigateur est libre de gérer la détection d'intersection comme bon lui semble.</p>
<p>Il y a une chose sur laquelle l'API <em>Intersection Observer</em> ne peut pas vous renseigner : le nombre de pixels qui intersectent, ou bien desquels il s'agit précisément; par contre elle permet d'ordonner des actions souvent utiles telles que « si ils s'intersectent de plus de N%, alors faire ceci ».</p>
<h2 id="Concepts_et_utilisation_de_l'observateur_d'intersections">Concepts et utilisation de l'observateur d'intersections</h2>
<p>L'API <em>Intersection Observer</em> permet de mettre en place une fonction <em>callback</em> qui est appelée quand un élément, appelé la <strong>cible</strong>, intersecte ou bien le <em>viewport</em> (la zone d'affichage) ou bien un élément prédéfini ; dans le cadre de cette API, nous l’appellerons <strong>l'élément racine</strong> ou <strong>la racine</strong>. Typiquement, on voudra observer les variations de l'intersection par rapport à la zone d'affichage du document (ce qui est fait en passant l'argument <code>null</code> au moment de désigner l'élément racine). Que vous utilisiez la zone d'affichage ou un autre élément comme racine, l'API fonctionne de la même façon, en exécutant une fonction <em>callback</em>, fournie au préalable, lorsque la visibilité de l'élément cible change de telle sorte qu'il atteint la quantité voulue d'intersection avec l'élément racine.</p>
<p>Le degré d'intersection entre l'élément cible et sa racine est le <strong>ratio d'intersection</strong>. C'est une représentation du pourcentage de l'élément cible qui est visible, exprimée sous la forme d'un nombre compris entre 0.0 et 1.0.</p>
<h3 id="Création_d'un_observateur_d'intersection">Création d'un observateur d'intersection</h3>
<p>Créez l'observateur d'intersection en appelant son constructeur et en lui passant la référence d'une fonction <em>callback</em>. Cette fonction sera exécutée quand un palier est franchi dans un sens ou dans un autre :</p>
<pre class="brush: js">var options = {
root: document.querySelector('#scrollArea'),
rootMargin: '0px',
threshold: 1.0
}
var observer = new IntersectionObserver(callback, options);</pre>
<p>Un palier de 1.0 signifie que lorsque 100% de la cible est visible dans l'élément désigné par l'option <code>root</code> (l'élément racine), la fonction <em>callback</em> est invoquée.</p>
<h4 id="Options_de_l'observateur_d'intersection">Options de l'observateur d'intersection</h4>
<p>L'objet <code>options</code> qui est passé dans le constructeur {{domxref("IntersectionObserver.IntersectionObserver", "IntersectionObserver()")}} permet de contrôler les circonstances selon lesquelles la fonction <em>callback</em> de l'observateur est invoquée. Il possède les champs suivants :</p>
<dl>
<dt><code>root</code></dt>
<dd>L'élément qui est utilisé comme zone d'affichage au moment d'évaluer la visibilité de la cible. Il doit être un ancêtre de la cible. S'il n'est pas spécifié ou s'il prend la valeur <code>null</code>, sa valeur par défaut est la zone d'affichage (le <em>viewport</em>) du navigateur.</dd>
<dt><code>rootMargin</code></dt>
<dd>La marge autour de la racine. Peut prendre des valeurs similaires à la propriété CSS {{cssxref("margin")}} par exemple "<code>10px 20px 30px 40px"</code> (top, right, bottom, left). Si l'élément <code>root</code> a été spécifié, les valeurs peuvent être exprimées en pourcentages. Cet ensemble de valeur sert à agrandir ou à réduire chaque coté du cadre délimitant l'élément racine avant d'évaluer les intersections. Par défaut, toutes les valeurs prennent la valeur zéro.</dd>
<dt><code>threshold</code></dt>
<dd>Soit un nombre, soit un tableau de nombre qui indique à quel pourcentage de la visibilité de la cible la fonction <em>callback</em> de la cible doit être exécuté. Si vous souhaitez seulement détecter quand la visibilité franchit la barre des 50%, vous pouvez entrer la valeur 0.5. Si vous voulez que le <em>callback</em> soit exécuté chaque fois que la visibilité varie de 25% de plus, il faudra spécifier le tableau [0, 0.25, 0.5, 0.75, 1]. La valeur par défaut est 0 (ce qui signifie que dés qu'un seul pixel sera visible, la fonction <em>callback</em> sera exécutée). Une valeur de 1.0 signifie que le palier n'est considéré comme franchi qu'une fois que tous les pixels sont visibles.</dd>
</dl>
<h4 id="Choisir_un_élément_à_observer">Choisir un élément à observer</h4>
<p>Une fois l'observateur créé, il faut lui donner un élément cible à observer :</p>
<pre class="brush: js">var target = document.querySelector('#listItem');
observer.observe(target);
</pre>
<p>Lorsque la cible franchit un palier spécifié indiqué dans l'objet <code>IntersectionObserver</code>, la fonction <em>callback</em> est appelée. Le <em>callback</em> reçoit une liste d'objets {{domxref("IntersectionObserverEntry")}} ainsi que l'observateur :</p>
<pre class="brush: js">var callback = function(entries, observer) {
entries.forEach(entry => {
// chaque élément de <em>entries</em> correspond à une variation
// d'intersection pour un des éléments cible:
// entry.boundingClientRect
// entry.intersectionRatio
// entry.intersectionRect
// entry.isIntersecting
// entry.rootBounds
// entry.target
// entry.time
});
};
</pre>
<p>Soyez attentif au fait que la fonction <em>callback</em> est exécutée dans le <em>thread</em> principal. Elle devrait être exécutée aussi rapidement que possible ; si une opération prenant du temps a besoin d'être effectuée, utilisez {{domxref("Window.requestIdleCallback()")}}.</p>
<p>De plus, remarquez que si vous avez spécifié l'option <code>root</code>, la cible doit être un descendant de l'élément <code>root</code>.</p>
<h3 id="Comment_est_calculée_l'intersection">Comment est calculée l'intersection</h3>
<p>Toutes les régions envisagées par l'API <em>Intersection Observer</em> sont des rectangles; les éléments de forme irrégulière sont assimilées au plus petit rectangle qui contient l'élément en question tout entier. De même, si la partie visible d'un élément n'est pas rectangulaire, le rectangle d'intersection de l'élément sera le plus petit rectangle qui contient toute la partie visible de l'élément.</p>
<p>Il est utile de comprendre comment les différentes propriétés fournies par {{domxref("IntersectionObserverEntry")}} décrivent une intersection.</p>
<h4 id="La_racine_de_l'intersection_et_la_marge_de_la_racine">La racine de l'intersection et la marge de la racine</h4>
<p>Avant de pouvoir étudier l'intersection d'un élément avec un conteneur, nous devons savoir quel est ce conteneur. Ce conteneur est <strong>la racine de l'intersection</strong>, ou <strong>élément racine</strong>. Ce peut être soit un élément du document qui est un ancêtre de l'élément à observer, ou <code>null</code> si l'on souhaite utiliser la zone d'affichage (<em>viewport</em>) du document comme conteneur.</p>
<p>Le rectangle utilisé pour délimiter la racine de l'intersection peut être ajusté en ajustant la <strong>marge de la racine</strong>, c'est-à-dire le champ <code>rootMargin</code>, lors de la création de {{domxref("IntersectionObserver")}}. La valeur de <code>rootMargin</code> définit le décalage ajouté à chaque coté du cadre délimitant la racine de l'intersection pour créer le cadre final de la racine de l'intersection (accessible via {{domxref("IntersectionObserverEntry.rootBounds")}} quand la fonction <em>callback</em> est exécutée).</p>
<h4 id="Paliers">Paliers</h4>
<p>Plutôt que de rapporter le moindre changement de variation de la visibilité d'un élément, l'API Intersection Observer utilise des <strong>paliers</strong>. Lors de la création d'un observateur, vous pouvez fournir une ou plusieurs valeurs numériques qui représentent des pourcentages de visibilité de l'élément cible. Dans ce cas, l'API ne rapportent que les changements de visibilité qui franchissent ces paliers.</p>
<p>Par exemple, si vous voulez être informé à chaque fois que la visibilité d'une cible passe au dessus ou en dessous de chaque multiple de 25%, il faudra fournir le tableau [0, 0.25, 0.5, 0.75, 1] comme liste de paliers lors de la création de l'observateur. Vous pouvez préciser dans quelle direction a changé la visibilité (c'est-à-dire, si l'élément est devenu plus ou moins visible) en lisant la valeur de la propriété {{domxref("IntersectionObserverEntry.isIntersecting", "isIntersecting")}} du {{domxref("IntersectionObserverEntry")}} passé dans la fonction <code>callback</code> lors du changement de visibilité. Si <code>isIntersecting</code> est <code>true</code>, l'élément cible est devenu au moins aussi visible quand le palier a été franchi. Si elle vaut <code>false</code>, la cible n'est plus aussi visible que le palier spécifié.</p>
<p>Pour mieux comprendre comment fonctionnent les paliers, faites défiler la boîte ci-dessous. A l'intérieur, chacune des boîtes colorées affiche son pourcentage de visibilité sur chacun de ses quatre coins, de telle sorte que l'on peut voir ces ratios changer tandis que le conteneur défile. Chaque boîte a un ensemble différent de paliers :</p>
<ul>
<li>La première boîte a un palier pour chaque point de pourcentage de visibilité; c'est à dire que le tableau {{domxref("IntersectionObserver.thresholds")}} est <code>[0.00, 0.01, 0.02, ..., 0.99, 1.00]</code>.</li>
<li>La deuxième boîte a un unique palier, à 50%.</li>
<li>La troisième boîte a des paliers tous les 10% de visibilité (0%, 10%, 20%, etc.).</li>
<li>La dernière boîte a des paliers tous les 25%.</li>
</ul>
<h5 id="exemple_de_seuil">Exemple de seuil</h5>
<pre class="brush: html hidden"><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 hidden">.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 hidden">let observers = [];
startup();
function 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.1) {
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
//wrapper.scrollIntoView({
// block: "start",
//});
document.scrollingElement.scrollTop = wrapper.firstChild.getBoundingClientRect().top + window.scrollY;
document.scrollingElement.scrollLeft = 750;
}
function intersectionCallback(entries) {
entries.forEach(function(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;
});
}
</pre>
<p>{{EmbedLiveSample("exemple_de_seuil", 500, 500)}}</p>
<h2 id="Interfaces">Interfaces</h2>
<dl>
<dt>{{domxref("IntersectionObserver")}}</dt>
<dd>L'interface principale pour l'API Intersection Observer. Elle fournit des méthodes pour créer et manipuler un observateur qui peut observer n'importe quel nombre d'éléments cibles pour une même configuration d'intersection. Chaque observateur peut observer de manière asynchrone les évolutions de l'intersection entre un ou plusieurs éléments cibles et un élément ancêtre partagé, ou avec le {{Glossary('viewport')}} de leur {{domxref("Document")}} de niveau supérieur. L'ancêtre ou le <em>viewport</em> est désigné par le terme <strong>racine</strong>.</dd>
<dt>{{domxref("IntersectionObserverEntry")}}</dt>
<dd>Cette interface décrit l'intersection d'un élément cible spécifique avec la racine de l'observateur à un moment donné. Les objets de ce type ne peuvent être obtenus que de deux façons : comme entrée du callback de votre <code>IntersectionObserver</code>, ou en appelant {{domxref("IntersectionObserver.takeRecords()")}}.</dd>
</dl>
<h2 id="Un_exemple_simple">Un exemple simple</h2>
<p>Cet exemple simple va faire changer la couleur et la transparence d'un élément cible à mesure qu'il devient plus ou moins visible. Sur la page <em><a href="/fr/docs/Web/API/Intersection_Observer_API/Timing_element_visibility">Timing element visibility with the Intersection Observer API</a></em>, vous pourrez trouver un exemple plus développé montrant comment chronométrer le temps durant lequel un élément (par exemple, une publicité) a été visible à l'écran, et comment réagir à cette information en enregistrant des statistiques ou en mettant à jour des éléments.</p>
<h3 id="HTML">HTML</h3>
<p>Le code HTML de cet exemple est très court. L'élément principal est la boîte que nous allons cibler (avec l'ingénieuse dénomination <code>"box"</code>) et certains contenus dans la boîte.</p>
<pre class="brush: html"><div id="box">
<div class="vertical">
Bienvenue dans <strong>La Boîte !</strong>
</div>
</div></pre>
<h3 id="CSS">CSS</h3>
<p>Le CSS n'est pas extrêmement important dans le cadre de cet exemple ; il met en place l'élément et établit que les attributs {{cssxref("background-color")}} et {{cssxref("border")}} peuvent participer à des <a href="/fr/docs/Web/CSS/CSS_Transitions">transitions CSS</a>. Nous utiliserons ces transitions pour faire évoluer l'élément de telle sorte qu'il devienne plus ou moins obscur.</p>
<pre class="brush: css">#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>Enfin, jetons un œil au code JavaScript qui utilise l'API <em>Intersection Observer</em>.</p>
<h4 id="Préparation">Préparation</h4>
<p>Premièrement, nous devons préparer des variables et installer l'observateur.</p>
<pre class="brush: js">var numSteps = 20.0;
var boxElement;
var prevRatio = 0.0;
var increasingColor = "rgba(40, 40, 190, ratio)";
var decreasingColor = "rgba(190, 40, 40, ratio)";
// On met l'ensemble en place.
window.addEventListener("load", function(event) {
boxElement = document.querySelector("#box");
createObserver();
}, false);</pre>
<p>Les constantes et variables que nous préparons sont :</p>
<dl>
<dt><code>numSteps</code></dt>
<dd>Une constante qui indique combien de paliers nous voulons avoir entre les ratios de visibilité de 0.0 et de 1.0.</dd>
<dt><code>prevRatio</code></dt>
<dd>Cette variable sera utilisée pour mémoriser quel était le ratio de visibilité la dernière fois qu'un palier a été franchi ; ce qui nous permettra de savoir si l'élément est en train de devenir plus ou moins visible.</dd>
<dt><code>increasingColor</code></dt>
<dd>Une chaîne de caractères définissant une couleur que nous appliquerons à l'élément cible quand le ratio de visibilité augmente. Le mot "ratio" dans cette chaîne de caractères sera remplacé par la ratio de visibilité de la cible actuelle, de telle sorte que l'élément, en plus de changer de couleur, deviendra de plus en plus opaque à mesure qu'il deviendra obscur.</dd>
<dt><code>decreasingColor</code></dt>
<dd>De même, il s'agit d'une chaîne de caractères qui définit une couleur que nous appliquerons lorsque le ratio de visibilité diminue.</dd>
</dl>
<p>On appelle {{domxref("EventTarget.addEventListener", "Window.addEventListener()")}} pour commencer à écouter l'évènement {{event("load")}} ; une fois que la page a finit de charger, on obtient une référence de l'élément avec l'identifiant <code>"box"</code> grâce à {{domxref("Document.querySelector", "querySelector()")}}, puis on appelle la méthode <code>createObserver()</code> que l'on va définir un peu plus tard pour gérer la création et l'installation de l'observateur d'intersection.</p>
<h4 id="Création_de_l'observateur_d'intersection">Création de l'observateur d'intersection</h4>
<p>La méthode <code>createObserver()</code> est appelée une fois que le chargement de la page est terminé afin de gérer la création du nouveau {{domxref("IntersectionObserver")}} et de commencer le processus d'observation de l'élément cible.</p>
<pre class="brush: js">function createObserver() {
var observer;
var options = {
root: null,
rootMargin: "0px",
threshold: buildThresholdList()
};
observer = new IntersectionObserver(handleIntersect, options);
observer.observe(boxElement);
}</pre>
<p>On commence par définir un objet <code>options</code> contenant les paramètres pour l'observateur. On voudra observer les évolutions de la visibilité de l'élément cible relativement au <em>viewport</em>, on initialise donc <code>root</code> avec <code>null</code>. Nous n'avons pas besoin de marge, donc l'espace de marge, <code>rootMargin</code>, est initialisé à <code>"0px"</code>. Ainsi, l'observateur surveillera les variations de l'intersection entre les frontières de l'élément cible et ceux du <em>viewport</em>, sans aucun espace ajouté ou ôté.</p>
<p>La liste de paliers de ratio de visibilité, <code>threshold</code>, est construite par la fonction <code>buildThresholdList()</code>. La liste de paliers est construite mécaniquement dans cet exemple car il y en a un certain nombre, et que ce nombre a vocation à être ajustable.</p>
<p>Une fois que <code>options</code> est prêt, nous pouvons créer le nouvel observateur, en appelant le constructeur {{domxref("IntersectionObserver.IntersectionObserver", "IntersectionObserver()")}} , en précisant une fonction <em>callback</em> à appeler quand l'intersection franchit l'un de nos paliers, <code>handleIntersect()</code>, et notre ensemble d'options. On appelle alors {{domxref("IntersectionObserver.observe", "observe()")}} sur l'observateur retourné, afin de le passer à l'élément qui sera notre cible</p>
<p>On pourrait également choisir de surveiller l'évolution de la visibilité de l'intersection de plusieurs éléments par rapport au <em>viewport</em> en appelant <code>observer.observe()</code>pour chacun de ces éléments.</p>
<h4 id="Construction_du_tableau_de_paliers_de_ratios">Construction du tableau de paliers de ratios</h4>
<p>La fonction<code>buildThresholdList()</code>, qui construit la liste de paliers, ressemble à ceci :</p>
<pre class="brush: js">function buildThresholdList() {
var thresholds = [];
for (var i=1.0; i<=numSteps; i++) {
var ratio = i/numSteps;
thresholds.push(ratio);
}
thresholds.push(0);
return thresholds;
}</pre>
<p>Cela construit la tableau de paliers (chacun de ces paliers étant un ratio compris entre 0.0 et 1.0, ajouté en poussant la valeur<code>i/numSteps</code>dans le tableau <code>thresholds</code> pour chaque entier <code>i</code> entre 1 et <code>numSteps</code>). On pousse également 0 pour inclure cette valeur. Le résultat, dans le cas où<code>numSteps</code>a sa valeur par défaut, est la liste de paliers suivante :</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>Bien sur, on pourrait coder en dur le tableau de paliers dans notre code, et c'est souvent ce que vous ferez. Cependant, cet exemple laisse un peu de place pour plus de configuration afin d'ajuster la granularité, par exemple.</p>
<h4 id="Gérer_les_évolutions_de_l'intersection">Gérer les évolutions de l'intersection</h4>
<p>Quand le navigateur détecte que l'élément cible (dans notre cas, celui avec l'identifiant <code>"box"</code>) a été révélé ou caché de tel sorte que son ratio de visibilité franchit l'un des paliers de notre liste, il appelle la fonction <code>handleIntersect()</code>:</p>
<pre class="brush: js">function handleIntersect(entries, observer) {
entries.forEach(function(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>Pour chaque {{domxref("IntersectionObserverEntry")}} dans la liste <code>entries</code>, on cherche si le {{domxref("IntersectionObserverEntry.intersectionRatio", "intersectionRatio")}} de l'entrée augmente; si c'est le cas, on donne à la propriété CSS {{cssxref("background-color")}} de la cible la valeur <code>increasingColor</code> (pour rappel, c'est la chaîne de caractères <code>"rgba(40, 40, 190, ratio)"</code>), et on remplace le mot "ratio" avec le <code>intersectionRatio</code> de l'entrée. Le résultat : non seulement la couleur change, mais la transparence de l'élément change aussi. Quand le ratio d'intersection diminue, l'alpha de la couleur de fond diminue de même, et l'élément devient plus transparent.</p>
<p>De même, si le <code>intersectionRatio</code> augmente, on utilise la chaîne de caractères <code>decreasingColor</code> et on y remplace le mot "ratio" avec <code>intersectionRatio</code> avant d'assigner le <code>background-color</code> de l'élément cible.</p>
<p>Enfin, afin de surveiller si le ratio d'intersection augmente ou diminue, on garde en mémoire le ratio actuel dans la variable <code>prevRatio</code>.</p>
<h3 id="Résultat">Résultat</h3>
<p>Vous pouvez retrouver le résultat ci-dessous. Déroulez cette page vers le haut ou vers le bas et observez comment l'apparence de la boîte change au fur et à mesure.</p>
<p>{{EmbedLiveSample('Un_exemple_simple', 400, 400)}}</p>
<p>Vous pouvez consulter un exemple encore plus détaillé sur l'article <em><a href="/fr/docs/Web/API/Intersection_Observer_API/Timing_element_visibility">Timing element visibility with the Intersection Observer API</a></em>.</p>
<h2 id="Spécifications">Spécifications</h2>
<table class="standard-table">
<tbody>
<tr>
<th scope="col">Spécification</th>
<th scope="col">État</th>
<th scope="col">Commentaire</th>
</tr>
<tr>
<td>{{SpecName('IntersectionObserver')}}</td>
<td>{{Spec2('IntersectionObserver')}}</td>
<td>Définition initiale.</td>
</tr>
</tbody>
</table>
<h2 id="Compatibilité_des_navigateurs">Compatibilité des navigateurs</h2>
<p>{{Compat("api.IntersectionObserver")}}</p>
<h2 id="Voir_aussi">Voir aussi</h2>
<ul>
<li><a href="https://github.com/WICG/IntersectionObserver/tree/gh-pages/polyfill">Une prothèse (<em>polyfill</em>) pour l'API <em>Intersection Observer</em></a></li>
<li><em><a href="/fr/docs/Web/API/Intersection_Observer_API/Timing_element_visibility">Timing element visibility with the Intersection Observer API</a></em></li>
<li>{{domxref("IntersectionObserver")}} et {{domxref("IntersectionObserverEntry")}}</li>
</ul>
|