react-metrics
Version:
An analytics library for React.js
107 lines (89 loc) • 2.86 kB
JavaScript
import EventListener from "fbjs/lib/EventListener";
import attr2obj from "./utils/attr2obj";
function isLeftClickEvent(event) {
return event.button === 0;
}
function isModifiedEvent(event) {
return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
}
export class TrackBindingPlugin {
constructor(
{attributePrefix = "data-metrics", traverseParent = false} = {}
) {
this._attributePrefix = attributePrefix;
this._traverseParent = traverseParent;
}
listen(callback, rootElement = document.body) {
if (typeof callback !== "function") {
throw new Error("callback needs to be a function.");
}
if (rootElement && rootElement.nodeType !== 1) {
throw new Error("rootElement needs to be a valid node element.");
}
if (this._clickHandler) {
this.remove();
}
this._rootElement = rootElement;
this._clickHandler = EventListener.listen(
rootElement,
"click",
this._handleClick.bind(this, callback)
);
return {
target: this,
remove: this.remove.bind(this)
};
}
remove() {
if (this._clickHandler) {
this._clickHandler.remove();
this._clickHandler = null;
}
}
/**
* A click handler to perform custom link tracking, any element with 'metrics-*' attribute will be tracked.
*
* @method _handleClick
* @param {Object} event
* @private
*/
_handleClick(callback, event) {
if (isModifiedEvent(event) || !isLeftClickEvent(event)) {
return;
}
let elem = event.target || event.srcElement;
let dataset = this._getData(elem);
if (this._traverseParent) {
const rootElement = this._rootElement;
while (elem !== rootElement) {
elem = elem.parentElement || elem.parentNode;
dataset = {...this._getData(elem), ...dataset};
}
}
if (!Object.keys(dataset).length) {
return;
}
const eventName = dataset && dataset.eventName;
const mergePagedefaults = dataset && dataset.mergePagedefaults;
delete dataset.mergePagedefaults;
if (eventName) {
delete dataset.eventName;
callback(eventName, dataset, mergePagedefaults === "true");
}
}
_getData(elem) {
return attr2obj(elem, this._attributePrefix);
}
}
export default function useTrackBindingPlugin({
callback,
rootElement,
attributePrefix,
traverseParent
}) {
const trackBindingPlugin = new TrackBindingPlugin({
attributePrefix,
traverseParent
});
return trackBindingPlugin.listen(callback, rootElement);
}