3

I have a main div (parent) with a input (child) and 2 other child div (clickable).

I want to capture focus out event for the main div - not when input or other 2 clickable div are clicked or focused. I have set up an event handler using jQuery to capture focusin focusout events on all the elements.

What I see is when I click on the input:

  1. first input is focused
  2. then the main div.

If I click on any other clickable divs

  1. event fires first focusout for input
  2. then main div and then other div gets focus in and main div get focusin.

I don't want main div to loose focus when clicked on other clickable divs.

How can I achieve this? I want to validate the input on lose focus but not when clicked other divs.

So far this is what I have : Fiddle

HTML :

<div id="main">
    <input id="i" type="text" />
    <div id="Div2" class="icons" style="background-color:blue; right: 25px;" onclick="log('Clear Clicked');"></div>
    <div id="Div1" class="icons" style="background-color:red;" onclick="log('DD Clicked');"></div>
</div>

CSS :

* {
    -webkit-box-sizing: border-box;
    /* Safari/Chrome, other WebKit */
    -moz-box-sizing: border-box;
    /* Firefox, other Gecko */
    box-sizing: border-box;
    /* Opera/IE 8+ */
}
#main {
    top: 50px;
    left: 200px;
    position: absolute;
    border: 1px solid #00bfff;
    width: 250px;
    height: 27px;
}
input {
    border: none;
    width: 248px;
    height: 25px;
    position: absolute;
    z-index: 200;
}
.icons {
    text-align:center;
    border:1px solid blue;
    height: 23px;
    width: 23px;
    position: absolute;
    top: 2px;
    right: 2px;
    z-index: 99999999999999999999999;
    background-position: center;
    background-repeat: no-repeat;
}

jQuery :

$("#main").focusin(function () {
    log("Main div got focused");
});

$("#i").focusin(function () {
    log("input got focused");
});

$("#Div2").focusin(function () {
    log("dropdown div got focused");
});

$("#Div1").focusin(function () {
    log("clear div got focused");
});

$("#main").focusout(function () {
    log("Main div lost focused");
});

$("#i").focusout(function () {
    log("input lost focused");
});

$("#Div2").focusout(function () {
    log("dropdown div lost focused");
});

$("#Div1").focusout(function () {
    log("clear div lost focused");
});

function log(msg) {
    //window.console.log(msg);
    $("body").append("<p>" + msg + "</p>");
}

Any help or guidance appreciated

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Goutham ܢܢ
  • 186
  • 1
  • 4
  • 12

5 Answers5

21

Here is a best way to solve, because I had the same problem too ^_^

There is a attr of event: "relatedTarget"

relatedTarget will provide the next element of this event

So, if next element is not your box OR anything inside your box, trigger focus out.

But FIRST you have to let your <div> element focusable, you have to add tabindex='-1' on div like this

<div id="main" tabindex='-1'>

the script is short:

        $("#main, #main *").blur(function(e){
            if(!$(e.relatedTarget).is("#main, #main *")){
                //focus out.
            }
        });

The focus will lost and get inside #main, but you can do anything when the focus is lost from #main area.

This is a little different to your request, but I guess this may be what you want. If this is, the code would be very clean.

James Jou
  • 271
  • 1
  • 3
  • As per my experience relatedTarget does not work in all the latest browsers i.e in IE10/11 and Firefox and chrome and Safari(in Mac machine) Although the target element can be found in firefox - e.originalEvent.explicitOriginalTarget and chrome - e.relatedTarget and in IE10/11 document.activeElement. e.relatedTarget and e.originalEvent.explicitOriginalTarget works only if other element are like input, button etc.. does not work for divs and document.activeElement works for most of the elements whether its an input or a div and safari none of the above works – Goutham ܢܢ Jul 22 '14 at 11:37
  • Strangely, this solution generates a focusout event when I click an input element inside the div element! – Spartak Lalaj Apr 12 '16 at 08:31
1

Based on this accepted answer Is there a way to use event.preventDefault with focusOut? If not, why?

$(document).on('mousedown', function (event) {
        target = event.target;
        main = document.getElementById("main");
        input = document.getElementById("i");

        if (target == input) return true;

        parent = event.target.parentNode;

        if (parent == main) {
            event.preventDefault();  //prevent default DOM action
            event.stopPropagation();   //stop bubbling
            return false;   // return
        }
});
jfrm
  • 86
  • 3
1

OK, so it looks like you are constructing some sort of input widget. I see that the #main div is the outer container of the widget, the input is for entering text, and then you have two other divs serving as buttons or something. You want to validate the value of the input when the user tries to exit the widget, and you are trying to capture this event by listening for focusout on #main. This won't work because a div isn't an element that can receive focus. See the answer here for more.

I can prove to you that your div isn't focusable with a little experiment:

If you put e.stopPropagation() in both your focusin and focusout listeners for your input, you'll see that your main div is never actually focused or unfocused itself; it was just receiving the focusin and focusup events as they bubbled up the DOM tree from your input.

So, this means we have to tackle your problem from another angle.

Let's describe what it means for your widget to lose focus with a short user story:

  1. User clicks on the input/#main/#Div1/#Div2 -- widget gains focus
  2. User clicks on input (if he/she hasn't already) and types some text -- widget focus is not lost
  3. User clicks somewhere on #main -- widget focus is not lost
  4. User clicks #Div1 and then #Div2 -- widget focus is not lost
  5. User clicks somewhere else on the page -- widget focus is lost -> validation runs

We now know exactly which events during which states should cause validation to run.

First, let's keep track of the 'focus' state of the widget with a boolean variable:

var isFocused = false;

The widget starts out in the unfocused state and becomes focused when there is a click anywhere in #main or its children OR when the input is somehow focused (could be via tabbed-into with the keyboard):

$("#main").on('click',function(){
  isFocused = true;
});
$("#i").on('focus',function(){
  isFocused = true;
});

The only time the widget becomes unfocused is when a) it's focused and b) the user clicks somewhere else on the page:

$(document).on('click',function(){
  if(isFocused){
    isFocused = false;
    //kick-off the validation check!
  }
});

But since all events bubble-up the DOM tree by default, multiple clicks within #main will bubble up to document.body and trigger a validation check. To prevent this, we call stopPropagation on the click event in the #main's click handler:

$("#main").on('click',function(e){
  isFocused = true;
  e.stopPropagation();
});

That's it!

I hope I was correct about what you're after.

Here's a working fiddle with above code.

Community
  • 1
  • 1
Jonathan Wilson
  • 4,138
  • 1
  • 24
  • 36
  • That was a nice answer, but there are couple of issues with this. In my Form not all click events are bubbled to the document (using knockout where bubble is set false) and also lets say you have one more control below the widget (textinput) and if tabout happens from the widget to the next control then the validation does not fire as this is not a click event – Goutham ܢܢ Jul 22 '14 at 11:24
0

Here is simple way to do it (As far as i understood)

$('#Div1').click(function(){
    log('Clear Clicked');
   //$('#main').focusin();
    $('#i').focus();
});
$('#Div2').click(function(){
   log('DD Clicked');
     //$('#main').focusin();
    $('#i').focus();
});

here is fiddle

Sathya Raj
  • 1,079
  • 2
  • 12
  • 30
0

The other way to workaround is to add a small setTimeout before running focusout handler.

You can easily whitelist a list of elements to exclude with and clearTimeout when they get focusin.

Dennis C
  • 24,511
  • 12
  • 71
  • 99