Is there a way to use event.preventDefault with focusOut? If not, why?

Klik picture Klik · Jan 30, 2013 · Viewed 23.5k times · Source

I would like to prevent the default event from a focusOut (or blur) event and keep the focus set to the specific input field.

This is what I have already tried:

  • using event.preventDefault() at the beginning and at the end of the function (for test purposes, not for style)

  • return false; to prevent propagation

  • setting the focus back to the element directly in the element (but I found that the focus still switches because the focus switch is executed after the .on() function executes. I know I could use setTimeout, but I’d like to avoid it and figure out the heart of the issue.)

  • using blur instead of focusOut

My code is this:

    _triggerEventHide: function(inst, eventType) {
        inst.$target.on(eventType, function(event) {
            event.preventDefault();
            if (!$.suggestBox.$divCont.hasClass($.suggestBox.mouseOverClassName)) {
                $.suggestBox.$divCont.css({display: 'none'});   //reposition Suggestbox
            } else {
                inst.target.focus(); 
            }
            event.preventDefault();
            return false;
        });
     }

Note that $target represents the jQuery-wrapped element and target represents the native DOM element.

I’ve done some research and have found already a few articles relating to my question, but none of them answer this exact question.

  1. preventDefault does not work on focus event – Here, the answer given is an alternate route to using any kind of event manipulation.

  2. Focus back textbox on focus out if does not have required value – Here, the solution was to use setTimeout() to set the focus back to the original element, which is all right, but I’d like to understand the heart of the issue, why preventDefault() is not working here.

  3. jQuery preventDefault() not working – I tried some possible solutions from this thread, such as using event.stopImmediatePropagation() and event.stop(), but all for naught.

I appreciate any time, knowledge and solutions (including attempts) contributed in this matter. I would really like to learn…

Here is a jsFiddle to show you the problem… feel free to play around with it and try different solutions.

Answer

Klik picture Klik · Jan 31, 2013

After some more research I've found out that only certain events are 'cancelable'.

  • Quoted from http://help.dottoro.com/ljwolcsp.php

    You can check whether an event can be canceled with the cancelable property in all browsers except in Internet Explorer before version 9. Although the cancelable property exists in Firefox, it always returns true, regardless of the cancelable state of the event. There is no way to determine whether an event can be canceled in Internet Explorer before version 9.

    Note that the use of the preventDefault method and the returnValue property on a non-cancelable event does not cause an error. When an event handler returns false, the event will be canceled. You can use it instead of the preventDefault method and the returnValue property. See Example 2 below.

  • Each event in Javascript has a cancelable property that is defined as 'true' if the event can be prevented or 'false' if the event cannot be cancelled. Ref: http://help.dottoro.com/ljeosnqv.php

  • An example script from http://help.dottoro.com/ljeosnqv.php demonstrating this property can be found here on jsFiddle (to save space). Note the comments in the code about how Firefox always returns true for the cancelable property in the event object.

The general principal being:

 $('selector').on(eventType, function (event) {
    alert(('cancelable' in event));   //will return true
    alert(event.cancelable);      //will return true if event can be cancelled
                                  //NOTE: Firefox mistakenly always returns true
});
  • Events have a preset order of occuring depending on the situation. For example if a mouse button is clicked the order of eventTriggers would be: mousedown, mouseup, and click. Ref: www.w3.org/TR/DOM-Level-2-Events/events.html#Events-Event-initMouseEvent

  • Quoted from http://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Events-flow-cancelation

    A 'cancelable event' is an event associated with a default action which is allowed to be canceled during the DOM event flow. At any phase during the event flow, the triggered event listeners have the option of canceling the default action or allowing the default action to proceed. In the case of the hyperlink in the browser, canceling the action would have the result of not activating the hyperlink. Not all events defined in this specification are cancelable events.

  • Sometimes the sequence of events has it that the DOM default action will occur before the event you are looking to cancel even shows up, in these cases, you cannot cancel the default action because it has already taken place. That is to say the default action occurs before the dispatch of the event.


MY SOLUTION:

That being said I did manage to find a solution to my problem and more/less this problem. Essentially I had an eventListener with an element for focusin already and I wanted this element to keep focus.

<div id='display'></div>
$('#idBox').on('focusin', function () {
  $('#div').append('focused');
  //do something
}

Initially I tried using a setTimeout event when I clicked off, but I found that wasn't good because it triggered my focusin event again. So what we need is an event that is dispatched before the DOM's default action of switching focus. I did find that there is an event that meets this requirement, but is only available to IE... typical. For your information it is: "onbeforedeactivate" and it is cancelable. However since cross browser compatibility is important, we should not use this eventTrigger. Rather I found that the event mousedown occurs before the DOM starts its focus-changing behaviours.

So what we could do is:

$(document).on('mousedown', function(event) { 
       target = event.target;     // the element clicked on
       myTarget = document.getElementById("idBox");   // the element to keep focus
       if (target !== myTarget) {   
           event.preventDefault();  //prevent default DOM action
           event.stopPropagation();   //stop bubbling
           return false;   // return
       }
});

There are many other ways of going about it. I personally don't like setting an eventListener to the entire document, but to demonstrate the basics it serves its purpose.


Footnote: A great article to read to learn more about event handling in Javascript is http://help.dottoro.com/ljtdqwlx.php An amazing resource I stumbled across.