@benshi.ai/js-sdk
Version:
Benshi SDK
107 lines (82 loc) • 3.4 kB
text/typescript
import { EventEmitter } from 'events';
import { Dataset } from './typings'
export enum ImpressionObservedTypes {
View = 'view',
Hide = 'hide'
}
declare interface BsViewableImpressionObserver {
on<U extends keyof BsViewableImpressionObserverEvents>(
event: U, listener: BsViewableImpressionObserverEvents[U]
): this;
emit<U extends keyof BsViewableImpressionObserverEvents>(
event: U, ...args: Parameters<BsViewableImpressionObserverEvents[U]>
): boolean;
}
interface BsViewableImpressionObserverEvents {
[ImpressionObservedTypes.View]: (id: string, item: Dataset) => void,
[ImpressionObservedTypes.Hide]: (id: string) => void
}
class BsViewableImpressionObserver extends EventEmitter {
private mutationObserver = null
private intersectionObserver = null
private containerSelector = ''
private itemSelector = ''
constructor(intersectionThreshold: number) {
super()
let intersectionObserverOptions = {
root: null,
rootMargin: "0px",
threshold: intersectionThreshold
};
this.intersectionObserver = new IntersectionObserver(
(entries, observer) => this.intersectionHandler(entries, observer),
intersectionObserverOptions
);
}
intersectionHandler(entries, observer) {
entries.forEach((entry) => {
if (entry.isIntersecting) {
this.emit(ImpressionObservedTypes.View, entry.target.dataset.logId, entry.target.dataset)
} else {
this.emit(ImpressionObservedTypes.Hide, entry.target.dataset.logId)
}
})
}
start(containerClassname, itemClassname) {
this.containerSelector = `.${containerClassname}`
this.itemSelector = `.${itemClassname}`
const addItemsToIntersectionObserver = () => {
document.querySelectorAll(`${this.containerSelector} ${this.itemSelector}`)
.forEach(element => {
this.intersectionObserver.observe(element)
})
}
const target = document.querySelector(this.containerSelector);
if (target === null) {
throw new Error('container-does-not-exist')
}
// if the items are already there when this function is called, they will not
// be added to intersection observer, so we need to do it manually
addItemsToIntersectionObserver()
this.mutationObserver = new MutationObserver(mutations => {
// we don't care specific changes per mutation,
// just know what are the new items
this.intersectionObserver.disconnect() // to avoid having more than one observer per item
// within the container we are only interested in those child elements
// that matches the itemSelector
addItemsToIntersectionObserver()
});
const config = {
attributes: true,
childList: true,
characterData: true
};
this.mutationObserver.observe(target, config);
}
stop() {
this.mutationObserver.disconnect();
document.querySelectorAll(`${this.containerSelector} ${this.itemSelector}`)
.forEach(element => this.intersectionObserver.unobserve(element))
}
}
export default BsViewableImpressionObserver