[recovery mode] JQuery — разделение событий onСlick и onDblСlick

this.window.$.on( 'click', function( event ){ context.toggleActive( ); } )
                         .on( 'dblclick', function( event ){ context.toggleMaximized( ); } );


При разработке JS приложений часто возникает неудобство (всё-таки сложно назвать это проблемой), когда необходимо отделять событие клика от двойного клика из-за особенностей обработки данных событий — браузерами. Думаю многие из Вас сталкивались с тем что во всех браузерах вначале происходит событие onClick, а уже после — onDblClick. Не знаю как Вам, но мне это очень мешает спокойному написанию обработки событий подобного рода.

Но проблема вся в том что каких-то простых и удобных решений подобной задачи — нет. Поэтому пришлось взяться за дело, и написать своё решение.

Плагин
Основа плагина — перехват вызова регистрации события (on, off, click, dblclick), с последующей заменой на свои события.

$.fn.on = function( ) // ( types, selector, data, fn, one )
{
        var argumentList = $.extend( true, [], arguments );
        var eventType = argumentList[0];
        //....
        case eventType === 'click' || eventType === 'dblclick':
        {
                // Инициируем навешивание текущей функции с дополнительными обработками
                onMethod.call( this, 'reg' + eventType, eventSelector, eventData,
                        function( regEvent, event )
                        {
                                // Важная проверка, чтобы знать мы ли это в цепи передачи событий
                                if( this === regEvent.target )
                                {
                                        // Не даём передать этот сигнал дальше
                                        regEvent.preventDefault( );
                                        regEvent.stopPropagation( );
                                                
                                        // Сбрасываем параметры оригинального вызова
                                        event.isDefaultPrevented = function returnFalse( ){ return false; };
                                        event.isPropagationStopped = function returnFalse( ){ return false; };

                                        // Вызываем оригинальную функцию
                                        eventFunc.call( regEvent.target, event );
                                                                
                                        // Если исходя из параметров сигнал нужно передать дальше - передаём
                                        if( !event.isPropagationStopped( ) )
                                        {
                                                $( event.currentTarget.offsetParent ).trigger( 'click', [ event ] );    
                                        }
                                }
                        }
                );

                // Считываем события к событию
                var clickEvents = $._data( this[0], 'events' ) ? $._data( this[0], 'events' )[ 'click' ] : undefined;

                // Если еще не записали ни одного клика - записываем
                if( typeof clickEvents === 'undefined' )
                {
                        // Непосредственно клик
                        onMethod.call( this, 'click', eventSelector, eventData, singleDoubleClick );
                                                
                        // Для проверки перемещения мыши
                        onMethod.call( this, 'mousedown', eventSelector, eventData, startMoving );
                        onMethod.call( this, 'mouseup', eventSelector, eventData, endMoving );
                }
        }
        //....
}

После перехвата события — от момента первого клика запускается таймер который считает был ли сдвиг мышки за определённый период.
И если был сдвиг, или не произошло второго клика — то происходит событие onClick, в обратном случае onDblClick.

  var timeOut = 200; // Время ожидания
        var start = { x: 0, y: 0 }; // Точка начала
        var end = { x: 0, y: 0 }; // Точка конца
        var distance = 0; // Расстояние между началом и концом движения
        var maxDistance = 3;

        var onMethod = $.fn.on; // Записываем оригинальный on
        var offMethod = $.fn.off; // Записываем оригинальный off

        var singleDoubleClick = function( event )
        {
                // Для каждого объекта в цепи, по которой от сына отцу идет событие, нужно создать свой таймер и счетчик кликов
                // то есть один глобальный объект нельзя держать, если два сына, то клик стает дабл кликом
                event.currentTarget.clicks = ( event.currentTarget.clicks || 0 ) + 1;
                
                // По умолчанию - не даём сигнал дальше
                event.preventDefault( );
                event.stopPropagation( );
                
                var timeoutCallback = function( event )
                {
                        return function( )
                        {
                                // Очищаем таймер
                                clearTimeout( event.currentTarget.timer );
                                
                                // Учитываем кол-во кликов, дергание мыши и стопинг пропагации
                                if( event.currentTarget.clicks === 1 && distance < maxDistance )
                                {
                                        $( event.currentTarget ).trigger( 'regclick', [ event ] );      
                                }
                                else if( event.currentTarget.clicks > 1 && distance < maxDistance )
                                {
                                        event.type = 'dblclick';
                                        $( event.currentTarget ).trigger( 'regdblclick', [ event ] );
                                }
                                
                                // Обнуляем счётчик кликов
                                event.currentTarget.clicks = 0;
                        };
                };

                // Устанавливаем таймер
                event.currentTarget.timer = setTimeout( timeoutCallback( event ), timeOut );
        };

        // Начало движения
        var startMoving = function( event )
        {
                start = $.getMousePosition( event );
        };

        // Конец движения
        var endMoving = function( event )
        {
                end = $.getMousePosition( event );
                var dx = end.x - start.x;
                var dy = end.y - start.y;
                distance = Math.sqrt( dx * dx + dy * dy );
        };

Посмотреть на сам плагин можно вот здесь: GitHub
Надеюсь данное решение будет полезным ещё кому-то.

© Habrahabr.ru