Balancing website speed with capturing product impression

I've been tasked with capturing impressions of all the products visible in the viewport on a website that features thousands of products. To achieve this, I implemented a directory and utilized the IntersectionObserver, which was referenced within the product's HTML code. However, this implementation is impacting the performance of the mobile site as users scroll. How can I capture these impressions without slowing down the website?

export class IntersectionObserverDirective
  implements OnDestroy, OnInit, AfterViewInit {
  @Input() debounceTime = 0;
  @Input() threshold = 1;

  @Output() visible = new EventEmitter<HTMLElement>();
  isSSR: boolean = typeof window === 'undefined';

  private observer: IntersectionObserver | undefined;
  private subject$ = new Subject<{
    entry: IntersectionObserverEntry;
    observer: IntersectionObserver;
  }>();

  constructor(private element: ElementRef) {}

  ngOnInit() {
      this.createObserver();
  }

  ngAfterViewInit() {
      this.startObservingElements();
  }

  ngOnDestroy() {
      if (this.observer) {
        this.observer.disconnect();
        this.observer = undefined;
      }
  
      this.subject$.next();
      this.subject$.complete();  
  }

  private isVisible(element: HTMLElement) {
    return new Promise(resolve => {
      const observer = new IntersectionObserver(([entry]) => {
        resolve(entry.intersectionRatio === 1);
        observer.disconnect();
      });

      observer.observe(element);
    });
  }

  private createObserver() {
    const options = {
      rootMargin: '0px',
      threshold: this.threshold,
    };

    const isIntersecting = (entry: IntersectionObserverEntry) =>
      entry.isIntersecting || entry.intersectionRatio > 0;

    this.observer = new IntersectionObserver((entries, observer) => {
      entries.forEach(entry => {
        if (isIntersecting(entry)) {
          this.subject$.next({ entry, observer });
        }
      });
    }, options);
  }

  private startObservingElements() {
    if (!this.observer) {
      return;
    }

    this.observer.observe(this.element.nativeElement);

    this.subject$
      .pipe(delay(this.debounceTime), filter(Boolean))
      .subscribe(async ({ entry, observer }) => {
        const target = entry.target as HTMLElement;
        const isStillVisible = await this.isVisible(target);

        if (isStillVisible) {
          this.visible.emit(target);
          observer.unobserve(target);
        }
      });
  }
}

Answer №1

Don't forget to include a timeout function within your createObserver() method like this.

private createObserver() {
        const options = {
            rootMargin: '0px',
            threshold: this.threshold,
        };
        let timeouts = {};
        const checkIntersection = (entry: IntersectionObserverEntry) =>
            entry.isIntersecting || entry.intersectionRatio > 0;
        this.observer = new IntersectionObserver((entries, observer) => {
            entries.forEach(entry => {
                if (checkIntersection(entry)) {
                    timeouts[entry.target.id] = setTimeout(() => {
                        this.subject.next({ entry, observer });
                    }, 500);
                }
                else {
                    clearTimeout(timeouts[entry.target.id])
                }
            });
        }, options);
    }

Answer №2

Utilizing Google Analytics for tracking scroll triggers is highly recommended due to its robust features and various tools available. It is advisable to entrust this task to experienced individuals or parties. GA is considered more standard, provides greater value to clients, and offers valuable insights into user behavior.

If you prefer not to use Google's services and want to create your own tracking tool from scratch, proceed with caution. The sheer volume of events triggered by scrolling and cursor movements can overwhelm multiple elements on a page. Consider using GA specifically for tracking clicks and generating heatmaps!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The issue with IE-9 is that it is mistakenly picking up the placeholder value as

My HTML code looks like this: <input id="SLOCriteriaOtherText" name="SLOCriteriaOtherText" style="width: 100%;" type="text" data-role="autocomplete" placeholder="Enter name for 'other' metric..." class="k-input" autocomplete="off" role="textb ...

Transferring data from a table to another window

Currently, I am retrieving values from a database and displaying them in a table. I would like to add a button to each row that will allow users to view more details about that specific row. At the moment, only ThesisID, AuthorID, and Title are visible. ...

Leveraging Parameters from URL in Javascript

I'm facing an issue with my area shape, the href="/kosmetikstudios/deutschland/Bayern" tag seems to be causing a problem. I want to utilize the parameter "Bayern" (which is the last parameter in the URL). I need this to be dynamic. Below is my JavaS ...

Switch out "FOR" in order to sum up every value within an array

Utilizing Javascript, I have an array defined as follows: counts: [ { id: 1, value: 0 }, { id: 2, value: 10 }, { id: 3, value: 5 }, { id: 4, value: 3 } ] I aim to calculate a variable named total that holds the sum of all valu ...

Unable to display divs in a stacked format using Bootstrap 5

I need help aligning div's vertically on the page. Currently, all my elements are displaying next to each other instead of stacking one below the other. body { height: 100vh; } <link href="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi ...

Sort your list efficiently with a custom hook in React using Typescript

I've been working on developing a custom hook in React that sorts an array based on two arguments: the list itself and a string representing the key to sort by. Despite trying various approaches, I haven't been able to find a solution yet. I&apos ...

What is the best way to trigger actions from child components within React Redux?

My server contains the following code snippet: <ReactRedux.Provider store={store}><Layout defaultStore={JSON.stringify(store.getState())}/></ReactRedux.Provider> The <Layout> component includes more nested components. Further dow ...

In order to determine if components linked from anchor elements are visible on the screen in Next.js, a thorough examination of the components

Currently, I am in the process of developing my own single-page website using Next.js and Typescript. The site consists of two sections: one (component 1) displaying my name and three anchor elements with a 'sticky' setting for easy navigation, a ...

What is the best way to identify duplicate data being returned from a server within a for loop?

When receiving sorted data from the server through an infinite scroll, my goal is to filter out duplicate entries and add new unique data to a client-side array. If duplicates are found, I want to stop the infinite scrolling process. $scope.collections = ...

The settimeout function does not seem to function properly within the context of

Currently, I am facing an issue with implementing block UI for blocking a specific div when a button is clicked. The problem I am encountering is that even though I want the blocked div to be unblocked after a certain delay, it remains permanently blocked ...

Click here to get the button click link

Is there a method to obtain the link of a button click on a website? This is the Website and it features a play button. I am looking for a way to capture the link triggered by clicking the play button so that the audio starts playing automatically each t ...

Display content from Angular 5 template directly in table without using parent template

Despite the fact that this question has been raised multiple times, I find myself in a slightly tricky situation. Here is the structure of my table: <table> <th>...</th> <app-custom-rows *ngFor="let t..." [customAttribute]="someval ...

Guide on integrating jQuery list filtering in Ionic 2

I stumbled upon a jQuery function that filters data in HTML, found it online. Now, I want to incorporate a similar feature into my Ionic app. However, I am unsure about the modifications required in list-search-min.js. Here is a snippet of my index.html: ...

Is it necessary to continue utilizing PropTypes when incorporating Typescript into a React application?

Typescript brings significant advantages in terms of type validation. Does it completely replace the necessity of using PropTypes? Or do PropTypes still offer unique benefits? ...

Typescript controller inheritance leading to Error: $injector:unpr Unknown Provider due to minification

LATEST UPDATE: 2019/07/16 The issue I am facing is actually a result of misusing $inject. Instead of declaring it as private $inject in api-service.ts, it should have been public static $inject = [...]. During the minification process, explicit injection ...

Insert DOM elements at the start of the parent element

I'm currently using the following JavaScript code to insert AJAX responses into a div with an ID of results: document.getElementById("results").innerHTML=xmlhttp.responseText; The issue I am encountering is that this code adds all new elements after ...

Ways to distinguish if Dynamics XRM javascript is being triggered from the unified interface (UCI) or the traditional web-client interface

Is there an official way to determine if code is being called from the UCI or the legacy web-client? I see the function Xrm.Internal.isUci(), but since it's labeled as Internal, it may not be recommended for use. However, I still need a method to diff ...

Having trouble receiving a complete response when making an ajax request to fetch an entire webpage

Currently, I am experimenting with J-Query Ajax functionality. My goal is to send a request in order to retrieve the entire HTML data of a page that I have hosted on bitbaysolutions.com. The issue arises when I load this URL and execute document.getElement ...

Setting up an Angular proxy for Server prod: A step-by-step guide

Exploring a new approach in my Angular front end app, I aim to conceal the API URL from the browser's network. For example, instead of directly calling api.url.dz/login, I wish to call front.url.dz/login on the front end and then redirect it to api.ur ...

Insert a new item into an Observable within Ionic 4

My lecturer has been guiding me as a beginner in Ionic development. Currently, I am working on an Ionic 4 shopping application where users can add items to their wishlist. The item IDs are stored in the wishlist collection in Firebase Firestore. However, I ...