UNPKG

3.25 kBJavaScriptView Raw
1/*
2 * Copyright 2020 Google LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16import { initMetric } from './lib/initMetric.js';
17import { observe } from './lib/observe.js';
18import { onHidden } from './lib/onHidden.js';
19import { onBFCacheRestore } from './lib/onBFCacheRestore.js';
20import { bindReporter } from './lib/bindReporter.js';
21import { getFCP } from './getFCP.js';
22let isMonitoringFCP = false;
23let fcpValue = -1;
24export const getCLS = (onReport, reportAllChanges) => {
25 // Start monitoring FCP so we can only report CLS if FCP is also reported.
26 // Note: this is done to match the current behavior of CrUX.
27 if (!isMonitoringFCP) {
28 getFCP((metric) => {
29 fcpValue = metric.value;
30 });
31 isMonitoringFCP = true;
32 }
33 const onReportWrapped = (arg) => {
34 if (fcpValue > -1) {
35 onReport(arg);
36 }
37 };
38 let metric = initMetric('CLS', 0);
39 let report;
40 let sessionValue = 0;
41 let sessionEntries = [];
42 const entryHandler = (entry) => {
43 // Only count layout shifts without recent user input.
44 if (!entry.hadRecentInput) {
45 const firstSessionEntry = sessionEntries[0];
46 const lastSessionEntry = sessionEntries[sessionEntries.length - 1];
47 // If the entry occurred less than 1 second after the previous entry and
48 // less than 5 seconds after the first entry in the session, include the
49 // entry in the current session. Otherwise, start a new session.
50 if (sessionValue &&
51 entry.startTime - lastSessionEntry.startTime < 1000 &&
52 entry.startTime - firstSessionEntry.startTime < 5000) {
53 sessionValue += entry.value;
54 sessionEntries.push(entry);
55 }
56 else {
57 sessionValue = entry.value;
58 sessionEntries = [entry];
59 }
60 // If the current session value is larger than the current CLS value,
61 // update CLS and the entries contributing to it.
62 if (sessionValue > metric.value) {
63 metric.value = sessionValue;
64 metric.entries = sessionEntries;
65 report();
66 }
67 }
68 };
69 const po = observe('layout-shift', entryHandler);
70 if (po) {
71 report = bindReporter(onReportWrapped, metric, reportAllChanges);
72 onHidden(() => {
73 po.takeRecords().map(entryHandler);
74 report();
75 });
76 onBFCacheRestore(() => {
77 sessionValue = 0;
78 fcpValue = -1;
79 metric = initMetric('CLS', 0);
80 report = bindReporter(onReportWrapped, metric, reportAllChanges);
81 });
82 }
83};