2014-01-13 4 views
-2

Я пытаюсь решить довольно простую задачу, но придерживаясь поведения JQuery.Отключенная кнопка вызывает событие клика после включения

У меня есть кнопка HTML, который я отключить (добавить отключен атрибут) сразу после клика, чтобы предотвратить несколько щелчков, сделать что-то долго работает (то есть обновление DOM с большим количеством элементов) и включить кнопку назад.

Проблема в том, что даже кнопка отключена очереди jquery, все клики на ней и поднимут мой обработчик кликов сразу после его включения.

Согласно документам JQuery, он не должен поднимать события для отключенного элемента.

Bellow - мой код. Откройте консоль JS, нажмите два раза на кнопку, обратите внимание на пару сообщений «START -» на консоли.

<div> 
<button id="mybtn" type="button">Find</button> 
</div> 

var btnElement = $('#mybtn'); 
btnElement.click(doClick); 

      function doClick() { 
       var btnElement = $('#mybtn'); 
       btnElement.attr('disabled', true); 

       console.log('START --' + Date()); 
       for (var i = 0; i < 70000; i++) { 
        var el = $('#mybtn'); 
        var w = el.width(); 
        w += 10; 
       } 
       console.log('STOP --' + Date()); 
       el.attr('disabled', false); 
      } 
+3

Отправьте свой код здесь, пожалуйста, и не пытайтесь уклониться от правила SO при отправке кода при ссылке на jsFiddle. – j08691

+0

Как вы можете добавить ссылку на скрипку, не публикуя какой-либо код? – DontVoteMeDown

+2

@DontVoteMeDown, обманывая его и обертывая ссылку в backticks. – brbcoding

ответ

1

Вот мое решение http://jsfiddle.net/DRyxd/8/

var btnElement = $('#mybtn'); 
var buttonIsBusy = false; 

function doHeavyJob() { 
    console.log('START --' + Date()); 
    for (var i = 0; i < 70000; i++) { 
     var el = $('#mybtn'); 
     var w = el.width(); 
     w += 10; 
    } 
    var timeoutId = setTimeout (unblockTheButton, 0); 
    console.log('STOP --' + Date()); 
} 

function unblockTheButton() { 
    console.log('unblockTheButton'); 
    btnElement.attr('disabled', false); 
    buttonIsBusy = false; 
} 

function doClick() { 
    console.log('click', buttonIsBusy); 
    if (buttonIsBusy) { 
     return; 
    } 

    btnElement.attr('disabled', true); 
    buttonIsBusy = true; 

    var timeoutId = setTimeout (doHeavyJob, 0); 
} 


btnElement.click(doClick); 

Проблема здесь в том, что функция щелкать-обработчик еще не завершен, и браузер не освежил DOM. Это означает, что block еще не был применен к кнопке. Вы можете попробовать подталкивая тяжелый код из текущего контекста, как это:

function someHeavyCode() { 
    /* do some magic */ 
} 
var timeoutId = setTimeout(someHeavyCode, 0); 

Это будет толкать тяжелый код из текущего context.Letting браузера обновить DOM первым и только после выполнения тяжелого кода.

В то время как выполняется тяжелый код, браузер (по крайней мере, Chrome) хранит пользовательскую очередь ввода где-то в другом месте (или наиболее вероятном другом потоке). И как только тяжелый код завершается - он передает DOM со всеми событиями в очереди. Мы должны каким-то образом игнорировать эти события. И я снова использую setTimeout с 0-кратным временем. Предоставление браузеру того, что было поставлено в очередь до разблокировка кнопки.

ВНИМАНИЕ Но будьте предельно осторожны с этой техникой. Браузер по-прежнему будет заблокирован, и если вы создадите много таких блоков, он может висеть. См. Также Why is setTimeout(fn, 0) sometimes useful? и рассмотрите возможность использования веб-мастеров.

P.S. Блокирование ввода пользователем таким образом не является хорошим подходом, попробуйте переосмыслить то, что вы собираетесь делать, возможно, для этого есть лучшее решение.

+0

Спасибо, вы спасли меня пару дней головной боли. – user3191484

+0

Хотя это решение замечательно, оно, похоже, не работает с Firefox (версия 54.0.1 на macOS). –

+1

@TadayoshiSato хорошо, как сказано в ответе - рассмотрите использование веб-мастеров https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers –

Смежные вопросы