jquery – How to wait for processing in focus to finish in blur handler


There was a problem creating a datepicker for a date field. There are 2 handlers on the field – blur and focus , in focus a datepicker is created, in blur it is deleted. Moreover, in focus it is created using a deferred object.

.directive('datePicker', [ '$compile', '$document', 'datePickerService', '$templateRequest', function ($compile, $document, datePickerService, $templateRequest) { 
    function link(scope, element, attrs) {
            var picker = null;
                var $elementFocusProcessing = $.Deferred();
                element.on('focus', function (event) {
                    var pos = angular.extend(element.offset(), { height: element[0].offsetHeight });                    

                    $templateRequest(datePickerService.datePickerTemplate).then(function (html) {
                        var template = angular.element(html);
                        picker = $compile(template)(scope);
                        picker.css({ top: pos.top + pos.height + 5, left: pos.left, display: 'block', position: 'absolute' });

                        picker.on('mousedown', function (evt) {

                element.on('blur', function () {
                    $elementFocusProcessing.then(function () {
                        if (picker) {
                            picker = null;
                        $elementFocusProcessing = $.Deferred();

        return {
            restrict: 'A',
            link: link,
            scope : {
                model: '=ngModel'

And the actual problem: if you select the date field, then switch to another window, and then click on any place on the page, except for the date field, several events occur in a row: focus fields, blur fields, click on the page. In this case, the creation of the picker in focus completes later than the attempt to remove it in blur , and it is not removed, although the focus of the element in the toga is lost. How to make code execution in blur happen only after code execution in focus is completed? So far it has only occurred to me to make an additional focusEnded variable to determine whether the code is being executed in focus , and to organize a cycle with a timeout in blur , but I feel that this can be done more elegantly using deferred objects.


Since there is no working example, we can only reason theoretically:

  1. Since the template does not change, it does not need to be loaded not only for each focus, but also for each link. Therefore, it can be taken out of the function and the result, and it is a promise , can be stored in a variable:

     var templatePromise = $templateRequest(datePickerService.datePickerTemplate);
  2. Next, the link function, inside it we mean that the template has already been loaded, for this we do everything else in the success handler of the then function of our saved templatePromise

     function link(scope,element,attrs){ templatePromise.then(function(html){ //достаточно сделать всего один элемент, // который мы будем добавлять или удалять var template = angular.element(html); //и добавим событие, чтобы не добавлять его каждый раз //так как template это уже объект jqLite можно сделать так template.on('mousedown', function (evt) { evt.preventDefault(); } ); //добавляем on focus element.on('focus', function (event) { ... $document.find('body').append(template); $compile(template)(scope); //так как template это объект jqLite, то вместо picker можно использовать его template.css(...) ... }); //добавляем on blur element.on('blur', function () { //так как добавляли template, то и удалять можно его //а так как он у нас всегда есть, то не надо проверять на null template.remove(); }); }); }
Scroll to Top