fxTip.js

/** 
 * @module fxTip
 * @desc The fxTip module provides the *create* and *remove* functions which are exposed by the
 * [default]{@link module:index~default} object, which is exported in [index.js]{@link module:index}.
 * It also provides a listener to detect the removal of DOM elements that have a tooltip
 * associated with them and exports a method to suspend polling of the DOM for removed elements.
 */

import {tips, tipsIndex} from './tips.js';
import {Tip} from './Tip.js';
import {checkBoolean} from './utils.js'

/**
 * The state of whether DOM polling is enabled
 * @type boolean
 * @since v2.1.1
 */
let DOMchecking = true;

/**
 * The timing metric used to determine how frequently the DOM should be queried for
 * removed elements. Triggers the [detectTargetRemoval]{@link module:utils~detectTargetRemoval}
 * event listener.
 * @type number
 * @const
 */
const targetTimerInterval = 500;

/**
 * The timer returned by window.setInterval. Used to suspend and
 * resume target removal detection.
 * @type number
 * @global
 */
export let targetTimer;

/** 
 * @function create
 * @desc Creates a new [Tip]{@link Tip} class object with all of the default parameters. If a tooltip
 * is already associated with DOM element represented by the *elementId* parameter, that tooltip is
 * deleted and replaced by a new one.
 * @param {string} elementId The unique id of the DOM element that be associated with the tooltip.
 * @param {string} content Any valid HTML content that will be displayed in the tooltip when shown.
 * @returns {(Tip | undefined)} A newly instantiated [Tip]{@link Tip} class object. If the element
 * represented by the elementId parameter is not in the DOM, returns undefined.
 */
export function create (elementId, content) {
    if (document.getElementById(elementId) == null) {
        return;
    };
    
    content = content == undefined ? '' : content;

    let index = tipsIndex.indexOf(elementId);

    if (index !== -1) {
        tips[index].remove();
    };

    let newTip = new Tip (elementId, content);
    tips.push(newTip);
    tipsIndex.push(elementId);

    if (targetTimer == undefined && DOMchecking) {
        targetTimer = window.setInterval(detectTargetRemoval, targetTimerInterval);
    };

    return tips[tips.length - 1];
 
}

/** 
 * @function remove
 * @desc Removes a specific tooltip from the stack (both the [tips]{@link module:tips~tips}
 * and [tipsIndex]{@link module:tips~tipsIndex} arrays) by calling the [Tip]{@link Tip} class
 * object's *remove* method. If the DOM element represented by
 * the *elementId* parameter is not in the DOM, no action is taken.
 * @param {string} elementId The unique id of the DOM element associated with the tooltip
 * to be removed.
 */
export function remove (elementId) {
    if (document.getElementById(elementId) == null) {
        return;
    };

    let index = tipsIndex.indexOf(elementId);
    if (index !== -1) tips[index].remove();

}

/**
 * @function checkDOM
 * @desc Controls whether the DOM will be periodically polled to see if DOM elements
 * that are associated with a tooltip have been remove from the document, and
 * automatically removes the [Tip]{@link Tip} class object. This may be useful if
 * your document has a large number of tooltips and you don't desire the additional
 * overhead of polling the DOM.
 * @param {boolean} checkDOM Whether to poll the DOM or not.
 * @since v2.1.1
 */
export function checkDOM (checkDOM) {
    if (checkDOM == undefined) return DOMchecking;
    if (checkBoolean(checkDOM)) {
        DOMchecking = checkDOM;
        if (DOMchecking) {
            targetTimer = window.setInterval(detectTargetRemoval, targetTimerInterval);            
        } else {
            window.clearInterval(targetTimer);
        };
    };
}

/**
 * @function detectTargetRemoval
 * @desc Iterates through the tooltip stack to determine if any DOM elements associated
 * with tooltips have been removed. These tooltips are then removed from the stack.
 * @event
 */
function detectTargetRemoval() {
    tipsIndex.forEach ((thisTip, i) => {
        if (document.getElementById(thisTip) == null) {
            tips[i].remove();
        };
    });
}