6

I use $(event.target).closest("#divID").length to hide a div when the user clicks outside of it but in the case that the div is visible and I click on a date (datepicker) it won't hide the div.

Also if I click on a <select> sometimes it hides it sometimes it doesn't.

Is there a better solution to hide a div when something else is clicked?

Is my implementation wrong?

ps: #log_in is the login button, #log_in_form is the form that I want to hide on outside click and #log_in_container is the div that contains #log_in and and #log_in_form

UPDATE: I just noticed that the disappearance isn't the same on windows 10 and linux ubuntu 16.04. On a pc with windows 10 using google chrome the form disappears on the first click i do on a select(thats the desirable functionality) but still doesn't disappear if i choose a date. While on linux ubuntu 16.04 on google chrome it is as i described above (doesn't disappear on date choice and also doesn't disappear on the first click you do on select)

Example based on Andrei's answer with snippet

$(document).on('click', function(e){
    if($(e.target).closest('#log_in').is('#log_in'))
    {
      $('#log_in_form').fadeIn();
    }
    else if(!$(e.target).closest('#log_in_container').is('#log_in_container'))
    {
      $('#log_in_form').fadeOut();
    }
})
#log_in_container{
  display:inline-block;
  width:122px;
  height:58px;
  margin-left:60px;
  background-color:gray;
}

#log_in{
  display:block;
  width:120px;
  height:28px;
  text-align: center;
  border: 1px solid red;
  font-size:16px;
  background-color:yellow;
  vertical-align: top;
}

#log_in_form{
  display:none;
  position:absolute;
  width:120px;
  height:28px;
  text-align: center;
  background-color: green;
  border: 1px solid blue;
}

.type{
  display:inline-block;
  width:120px;
  height:30px;
  text-align: center;
  border: 1px solid red;
  font-size:16px;
}
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

    <!-- ~~~~~~~~~~~~~~~Date picker ~~~~~~~~~~~~~~~ -->
 <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
 <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
 <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
 <script>
     $( function()
   {
       $( "#datepicker" ).datepicker();
     });
   </script>
    <title></title>
  </head>
  <body>
  <select class="type">
      <option value="null" disabled="disabled" selected="selected"><p style="color:gray;">Select hero</p></option>
      <option value="0">Spiderman</option>
      <option value="1">Iron man</option>
      <option value="2">Deadpool</option>
  </select>
  <select class="type">
      <option value="null" disabled="disabled" selected="selected"><p style="color:gray;">Select food</p></option>
      <option value="0">Kebab</option>
      <option value="1">Mousaka</option>
      <option value="2">Noodles</option>
  </select>
  <div id="log_in_container">
      <div  id="log_in">Log_In_button</div>
      <div id ="log_in_form">login_form</div>
  </div>
  <div id="datepicker"></div>


  </body>
</html>

In the above snippet if you click at Log_in_button the Log_in_form appears. If then you click on a date it doesn't disappear, then click on a select the Log_in_form still doesn't disappear, after that click on the next select the Log_in_form is still visible. I would like to make it so that it disappears in these occasions (like select pop up does). Is this possible?

captain monk
  • 719
  • 4
  • 11
  • 34
  • Is there a reason why you want to hide the `#log_in_form` in the first place? – Marylyn Lajato Nov 15 '17 at 02:54
  • I don't want user to press the log in button to close the log in form. It's more handy to just click somewhere else to close it. Also i will use this to a lot of divs i want to hide when user clicks outside of them. – captain monk Nov 15 '17 at 03:17
  • The update to the question almost renders my answer unrelated to it. Nonetheless, it looks like you forgot to add the markup of the login form so there's nothing to hide, is there? Please add all relevant code and specify in clear the intended/expected behavior. – tao Nov 15 '17 at 11:45
  • I thought print inside is equivalent to display:block the hiden div and print outside to display:none. I will add it if it helps – captain monk Nov 15 '17 at 12:08
  • So you want any click outside `#log_in_container` to hide this container, if it's open, is this correct? – tao Nov 15 '17 at 14:15
  • Yes andrei. Like – captain monk Nov 15 '17 at 17:07
  • 1
    If you run `console.log($(e.target).closest('#log_in').length);` you will see that every element on the page returns either `1` or `0`, but clicks on the dates in the datepicker does not return any value at all. It seems the click event is captured by jQuery UI and possibly disabled using `return false` or `event.stopPropagation()`. – agrm Nov 25 '17 at 21:21

5 Answers5

4

jQuery works only with event bubbling (the event starts at the target element and works up the DOM tree, raising click events until it reaches the top level document). The potential issue with this, as you can see with the ui-datepicker, is that elements can cancel the bubbling by using event.stopPropagation(), and the document never gets it.

Instead you need to use raw Javascript event capturing instead, where the top-level element captures the event happening and passes it down the DOM tree.

The diagram on the W3C site explains the capture and bubble flow nicely, and see this question for more details and cross-browser implementation.

The change required to your code is minimal, you are just using document.addEventListener instead of $(document).on:

document.addEventListener("click", function(e){
    if($(e.target).closest('#log_in').is('#log_in'))
    {
      $('#log_in_form').fadeIn();
    }
    else if(!$(e.target).closest('#log_in_container').is('#log_in_container'))
    {
      $('#log_in_form').fadeOut();
    }
}, true);
#log_in_container{
  display:inline-block;
  width:122px;
  height:58px;
  margin-left:60px;
  background-color:gray;
}

#log_in{
  display:block;
  width:120px;
  height:28px;
  text-align: center;
  border: 1px solid red;
  font-size:16px;
  background-color:yellow;
  vertical-align: top;
}

#log_in_form{
  display:none;
  position:absolute;
  width:120px;
  height:28px;
  text-align: center;
  background-color: green;
  border: 1px solid blue;
}

.type{
  display:inline-block;
  width:120px;
  height:30px;
  text-align: center;
  border: 1px solid red;
  font-size:16px;
}
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

    <!-- ~~~~~~~~~~~~~~~Date picker ~~~~~~~~~~~~~~~ -->
 <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
 <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
 <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
 <script>
     $( function()
   {
       $( "#datepicker" ).datepicker();
     });
   </script>
    <title></title>
  </head>
  <body>
  <select class="type">
      <option value="null" disabled="disabled" selected="selected"><p style="color:gray;">Select hero</p></option>
      <option value="0">Spiderman</option>
      <option value="1">Iron man</option>
      <option value="2">Deadpool</option>
  </select>
  <select class="type">
      <option value="null" disabled="disabled" selected="selected"><p style="color:gray;">Select food</p></option>
      <option value="0">Kebab</option>
      <option value="1">Mousaka</option>
      <option value="2">Noodles</option>
  </select>
  <div id="log_in_container">
      <div  id="log_in">Log_In_button</div>
      <div id ="log_in_form">login_form</div>
  </div>
  <div id="datepicker"></div>


  </body>
</html>

As you mentioned previously, the initial <select> does work in Chrome on Windows, but not Chrome on Linux. Browsers often hand off rendering of form controls to the OS for visual consistency, but this can introduce inconsistency in behaviour. <select>s are probably the most inconsistent of the base form controls, because of their added complexity. This question demonstrates this issue, particularly:

Chrome 19 on Linux: First mouse click expands options [click event not triggered], subsequent click on either the still-present select, or the options, triggers click event.

As this is a browser behaviour which you don't have control over, I don't think there is any way around it - you can only handle the events it fires. If the event is not fired by the browser, you can't react to it.

Rhumborl
  • 16,349
  • 4
  • 39
  • 45
  • Thanks for the info, I will thoroughly read what you recommended. In your snippet it does disappear on date click but it doesn't when you click on a select. Try clicking Log_in_button then on first select and then on second select. – captain monk Nov 25 '17 at 22:08
  • (without selecting one of the dropped down options) – captain monk Nov 25 '17 at 22:29
  • @captainmonk I've added some explanation of the issue with ` – Rhumborl Nov 26 '17 at 09:52
1

You can simply use mouseup event instead of click which will work even on date click.

$(document).on('mouseup', function(e){
    if($(e.target).closest('#log_in').is('#log_in'))
    {
      $('#log_in_form').fadeIn();
    }
    else if(!$(e.target).closest('#log_in_container').is('#log_in_container'))
    {
      $('#log_in_form').fadeOut();
    }
})
#log_in_container{
  display:inline-block;
  width:122px;
  height:58px;
  margin-left:60px;
  background-color:gray;
}

#log_in{
  display:block;
  width:120px;
  height:28px;
  text-align: center;
  border: 1px solid red;
  font-size:16px;
  background-color:yellow;
  vertical-align: top;
}

#log_in_form{
  display:none;
  position:absolute;
  width:120px;
  height:28px;
  text-align: center;
  background-color: green;
  border: 1px solid blue;
}

.type{
  display:inline-block;
  width:120px;
  height:30px;
  text-align: center;
  border: 1px solid red;
  font-size:16px;
}
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

    <!-- ~~~~~~~~~~~~~~~Date picker ~~~~~~~~~~~~~~~ -->
 <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
 <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
 <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
 <script>
     $( function()
   {
       $( "#datepicker" ).datepicker();
     });
   </script>
    <title></title>
  </head>
  <body>
  <select class="type">
      <option value="null" disabled="disabled" selected="selected"><p style="color:gray;">Select hero</p></option>
      <option value="0">Spiderman</option>
      <option value="1">Iron man</option>
      <option value="2">Deadpool</option>
  </select>
  <select class="type">
      <option value="null" disabled="disabled" selected="selected"><p style="color:gray;">Select food</p></option>
      <option value="0">Kebab</option>
      <option value="1">Mousaka</option>
      <option value="2">Noodles</option>
  </select>
  <div id="log_in_container">
      <div  id="log_in">Log_In_button</div>
      <div id ="log_in_form">login_form</div>
  </div>
  <div id="datepicker"></div>


  </body>
</html>
Makarand Patil
  • 1,001
  • 1
  • 8
  • 15
  • This is valid for the specific snippet, but there may be `mouseup` events which cancel bubbling and so break that, e.g. add `$( "#datepicker" ).on('mouseup', function(e) { e.stopPropagation(); });` underneath `$( "#datepicker" ).datepicker();` – Rhumborl Nov 27 '17 at 13:24
1

Your code is working except on dateoicker <td>. this is really weird

But we can still hack this using onSelect method from jQuery ui datepicker .

$(document).on('click', function(e){
    if($(e.target).closest('#log_in').is('#log_in'))
    {
      $('#log_in_form').fadeIn();
    }
    else if(!$(e.target).closest('#log_in_container').is('#log_in_container'))
    {
      $('#log_in_form').fadeOut();
    }
})
#log_in_container{
  display:inline-block;
  width:122px;
  height:58px;
  margin-left:60px;
  background-color:gray;
}

#log_in{
  display:block;
  width:120px;
  height:28px;
  text-align: center;
  border: 1px solid red;
  font-size:16px;
  background-color:yellow;
  vertical-align: top;
}

#log_in_form{
  display:none;
  position:absolute;
  width:120px;
  height:28px;
  text-align: center;
  background-color: green;
  border: 1px solid blue;
}

.type{
  display:inline-block;
  width:120px;
  height:30px;
  text-align: center;
  border: 1px solid red;
  font-size:16px;
}
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

    <!-- ~~~~~~~~~~~~~~~Date picker ~~~~~~~~~~~~~~~ -->
 <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
 <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
 <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
 <script>
     $( function()
   {
       $( "#datepicker" ).datepicker({
   onSelect: function(i, e){
    $('#log_in_form').fadeOut();
   }
   });
     });
   </script>
    <title></title>
  </head>
  <body>
  <select class="type">
      <option value="null" disabled="disabled" selected="selected"><p style="color:gray;">Select hero</p></option>
      <option value="0">Spiderman</option>
      <option value="1">Iron man</option>
      <option value="2">Deadpool</option>
  </select>
  <select class="type">
      <option value="null" disabled="disabled" selected="selected"><p style="color:gray;">Select food</p></option>
      <option value="0">Kebab</option>
      <option value="1">Mousaka</option>
      <option value="2">Noodles</option>
  </select>
  <div id="log_in_container">
      <div  id="log_in">Log_In_button</div>
      <div id ="log_in_form">login_form</div>
  </div>
  <div id="datepicker"></div>


  </body>
</html>
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
plonknimbuzz
  • 2,594
  • 2
  • 19
  • 31
0

I have a suggestion to use the event mousedown instead of click and also to check whether the click happened inside the log in wrapper like this

$(e.target).parents().has($('#log_in_container')).length

the entire document ready would look like this

$(document).on('mousedown', function(e){
    if($(e.target).closest('#log_in').is('#log_in'))
    {
      $('#log_in_form').fadeIn();
    }
    else if($(e.target).parents().has($('#log_in_container')).length)
    {
      $('#log_in_form').fadeOut();
    }
})
Aswin Ramesh
  • 1,654
  • 1
  • 13
  • 13
0

Why don't you use the onSelect event of the datepicker and trigger the click that you have bound to the document manually, as it does not seem to be triggered when you click on the dates inside the datepicker, no need to change anything regarding events or writing a separate logic inside the onSelect, just add any more logic if you want inside the .on('click') and trigger it just add 2 lines of your code and keep everything as is.

You just need to change your datepicker initialization like below

$("#datepicker").datepicker({
    onSelect: function (date, obj) {
        $(document).trigger('click')
    }
});

See the demo below

$(document).on('click', function(e) {
  if ($(e.target).closest('#log_in').is('#log_in')) {
    $('#log_in_form').fadeIn();
  } else if (!$(e.target).closest('#log_in_container').is('#log_in_container')) {
    $('#log_in_form').fadeOut();
  }
})
#log_in_container {
  display: inline-block;
  width: 122px;
  height: 58px;
  margin-left: 60px;
  background-color: gray;
}

#log_in {
  display: block;
  width: 120px;
  height: 28px;
  text-align: center;
  border: 1px solid red;
  font-size: 16px;
  background-color: yellow;
  vertical-align: top;
}

#log_in_form {
  display: none;
  position: absolute;
  width: 120px;
  height: 28px;
  text-align: center;
  background-color: green;
  border: 1px solid blue;
}

.type {
  display: inline-block;
  width: 120px;
  height: 30px;
  text-align: center;
  border: 1px solid red;
  font-size: 16px;
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

  <!-- ~~~~~~~~~~~~~~~Date picker ~~~~~~~~~~~~~~~ -->
  <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
  <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
  <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
  <script>
    $(function() {
      $("#datepicker").datepicker({
        onSelect: function(date, obj) {
          $(document).trigger('click')
        }
      });
    });
  </script>
  <title></title>
</head>

<body>
  <select class="type">
      <option value="null" disabled="disabled" selected="selected"><p style="color:gray;">Select hero</p></option>
      <option value="0">Spiderman</option>
      <option value="1">Iron man</option>
      <option value="2">Deadpool</option>
  </select>
  <select class="type">
      <option value="null" disabled="disabled" selected="selected"><p style="color:gray;">Select food</p></option>
      <option value="0">Kebab</option>
      <option value="1">Mousaka</option>
      <option value="2">Noodles</option>
  </select>
  <div id="log_in_container">
    <div id="log_in">Log_In_button</div>
    <div id="log_in_form">login_form</div>
  </div>
  <div id="datepicker"></div>


</body>

</html>
Muhammad Omer Aslam
  • 22,976
  • 9
  • 42
  • 68