3

I have a top bar icon system like Facebook (friends, msgs, notifications), I want to check if the user clicked on an icon then show the box related to that icon, and if clicked somewhere else on the page then hide the box. To do that I have to always listen to all clicks in the document and check if the click was outside the box or the icon then hide the box. And I don't like to "always listen to all clicks in the document". So to cut in performance I only start to listen for clicks in the document after the user clicks on the icon, and I stop listening for the document after the user clicked somewhere else outside the icon or the box.

The problem now is that I expect this code below to detect the click on the icon and then start listening for all clicks in the document and if the user clicked on another icon then stop listening for all clicks in the document then start the process again.

//this function just checks if the click was outside an element
//http://stackoverflow.com/questions/1403615/use-jquery-to-hide-a-div-when-the-user-clicks-outside-of-it
function clicked_outside(element, e){
        return !element.is(e.target) && element.has(e.target).length === 0;
}
$('.icon').on('click', function(e){
        var object = $(this);
        var box = object.find('.box');
        box.show();
        console.log('start listening for all html clicks outside the element (the icon and the box)');
        $('html').on('click', function(e){
                if (clicked_outside(object, e)){
                        box.hide();
                        console.log('stop listening for all html clicks');
                        $('html').off('click');
                }
        });
});

What happens is the code detect the click on the icon and then start listening for all clicks in the document and if the user clicked on another icon then it starts to listen for all clicks in the document again then stops listening for all clicks in the document

Why is the listening to clicks is not executing in the right order?

Sorry if my explanation was confusing and long, I swear I did my best, here is a jsfiddle to help out.

  • https://jsfiddle.net/arunpjohny/dgbpjnru/1/ ? – Arun P Johny Jun 14 '16 at 02:46
  • That's working fine, but I couldn't quite understand what did you do? can you explain please! – Said Esbaiy Jun 14 '16 at 02:49
  • Added `$('.icon .box').not(box).hide();` & `$('html').off('click.icon').on('click.icon', function(e) {` – Arun P Johny Jun 14 '16 at 02:51
  • @SaidEsbaiy: Basically, it's behaving that way because you told it to behave that way. Why did you expect it to stop listening for all clicks in the document when you haven't told it to stop listening? That's what Arun's `.off()` function does - tell it to stop listening – slebetman Jun 14 '16 at 03:01

2 Answers2

1

The reason why you have the issue is due to how events work. They start at the element that you click and they work their way up the dom until they hit have a parent anymore or some javascript stops the propagation. So in your code, you would always start the next box click before you could ever stop the previous box click.

Try this to fix that. Also don't use words like "object" as a variable name. It can have very weird results.

function clicked_outside(element, e){
        return !element.is(e.target) && element.has(e.target).length === 0;
}
var obj = null;
$('.icon').on('click', function(e){
        if(obj){
            obj.find('.box').hide();
            $('html').off('click');
        }
        obj = $(this);
        var box = obj.find('.box');
        box.show();
        console.log('start listening for all html clicks outside the element (the icon and the box)');
        $('html').on('click', function(e){
                if (clicked_outside(obj, e)){
                        box.hide();
                        console.log('stop listening for all html clicks');
                        $('html').off('click');
                }
        });
});

Here is another way that doesn't start/stop the listening.

function clicked_outside(element, e){
        return !element.is(e.target) && element.has(e.target).length === 0;
}
var obj = null;
$('.icon').on('click', function(e){
        if(obj){
            obj.find('.box').hide();
        }
        obj = $(this);
        obj.find('.box').show();            
});

$('html').on('click', function(e){
    if (obj && clicked_outside(obj, e)){
            obj.find('.box').hide();
            obj = null;
    }
});
Bryan Euton
  • 919
  • 5
  • 12
0

updated the fiddle, not sure what you want exactly.

var handler = function(e){
  if (clicked_outside(object, e)){
    box.hide();
    console.log('stop listening for all html clicks');
    $('html').off('click', handler);
  }
};
$('html').on('click', handler);

https://jsfiddle.net/dbjon4p5/1/

shams.kool
  • 343
  • 1
  • 2
  • 12