mistadikay
8/13/2014 - 11:29 AM

focusin/focusout polyfill

focusin/focusout polyfill

/**
 * usage: NAMESPACE.addFocusInEvent($node, listener);
 * replace comments containing [code] with your code
 */
(function(NAMESPACE, global){
    var document = global.document,
        $body = document.body,
        focusInEvent = 'focusin',
        focusOutEvent = 'focusout',
        $testInputContainer,
        $testInput,
        focusInspectionsDestructor,
        $activeElement,
        activeElementFakeEvent,
        inputsInInspection = 0,
        isNative = false;

    function testNativeFocusInOutEvents(){
        $testInputContainer = document.createElement('div');
        $testInputContainer.setAttribute('style', 'width:10xp;height:10px;overflow:hidden;position:fixed;top:-10px;left:-10px;');
        $body.appendChild($testInputContainer);

        $testInput = document.createElement('input');
        $testInput.type = 'text';
        $testInput.addEventListener('focusin', initNativeFocusInOutEvents);
        $testInputContainer.appendChild($testInput);

        $testInput.focus();

        $testInput.removeEventListener('focusin', initNativeFocusInOutEvents);
        // [code] remove $testInputContainer

        if (!isNative){
            initCustomFocusInOutEvents();
        }
    }

    function initNativeFocusInOutEvents(){
        isNative = true;
        NAMESPACE.addFocusInEvent = function($element, listener){
            $element.addEventListener(focusInEvent, listener);
        };
        NAMESPACE.removeFocusInEvent = function($element, listener){
            $element.removeEventListener(focusInEvent, listener);
        };
        NAMESPACE.addFocusOutEvent = function($element, listener){
            $element.addEventListener(focusOutEvent, listener);
        };
        NAMESPACE.removeFocusOutEvent = function($element, listener){
            $element.removeEventListener(focusOutEvent, listener);
        };
    }

    function initCustomFocusInOutEvents(){
        NAMESPACE.addFocusInEvent = function($node, listener){
            addEventForInspection();
            // [code] add event listener for focusInEvent to $node
        };
        NAMESPACE.removeFocusInEvent = function($node, listener){
            removeEventForInspection();
            // [code] remove event listener for focusInEvent to $node
        };
        NAMESPACE.addFocusOutEvent = function($node, listener){
            addEventForInspection();
            // [code] add event listener for focusOutEvent to $node
        };
        NAMESPACE.removeFocusOutEvent = function($node, listener){
            removeEventForInspection();
            // [code] remove event listener for focusOutEvent to $node
        };
    }

    function addEventForInspection(){
        if (inputsInInspection === 0){
            $activeElement = document.activeElement;
            activeElementFakeEvent = {target: $activeElement};
            focusInspectionsDestructor = focusInspectionsInit();
        }
        inputsInInspection += 1;
    }

    function removeEventForInspection(){
        inputsInInspection -= 1;
        if (inputsInInspection <= 0){
            inputsInInspection = 0;
            focusInspectionsDestructor();
        }
    }

    function focusInspectionsInit(){
        function check(){
            if ($activeElement !== document.activeElement){
                // [code] generate focusOutEvent event for $activeElement and pass activeElementFakeEvent as an argument

                $activeElement = document.activeElement;
                activeElementFakeEvent.target = $activeElement;

                // [code] generate focusInEvent event for $activeElement and pass activeElementFakeEvent as an argument
            }
        }
        // [code] add check to global timeout
        return function(onDestruction){
            activeElementFakeEvent.target = null;
            activeElementFakeEvent = $activeElement = null;
            // [code] remove check from global timeout
        }
    }

    testNativeFocusInOutEvents();
})(window, window);