Skip to content
Advertisement

In JavaScript e.target is not working as I expected

I have written some JavaScript that opens an element when an element is clicked. However I can’t get the:

 var menu = document.getElementById(show);
 if (menuOpen && e.target !== menu){...}

This is not working to how I want it because:

  1. You can open more than one of the showed elements when I only want one open at a time.

  2. When I click inside the element it closes, I only want it to close if they have clicked outside the box.

     function openBox(button, show){
         var menuOpen = false; //to toggle when the button is clicked.
    
         // checks the whole document for clicks and then if the element is open it will >
         // check to see if you have clicked away from it or not.
         document.addEventListener("click", function(e){
             var menu = document.getElementById(show);
             if (menuOpen && e.target !== menu){       // if elements open and the click event target does not match >
                 menu.style.display = "none";          // we will close it
                 menuOpen = false;
             }
         },false);
    
         // add an event listner to the button element and then if its clicked stop any >
         // links an stop bubbling and then change the display style.
         document.getElementById(button).addEventListener("click", function(e){
             var menu = document.getElementById(show);
             e.preventDefault();
             e.stopPropagation();
             if (menuOpen){
                 menu.style.display = "none";
                 menuOpen = false;
             } else {
                 menu.style.display = "block";
                 menuOpen = true;
             }
         },false);
     }
     openBox("signInButton", "signIn");
     openBox("bagButton", "shoppingBag");
     openBox("currencyButton", "currencySelect");
    

http://jsfiddle.net/jamcoupe/9CEGw/

Edit: After @Felix Kling post I changed the code to:

document.addEventListener("click", function(e){
    var menu = document.getElementById(show);
    if (menuOpen && (e.target.parentNode !== menu) && (e.target !== menu)){    
        menu.className = "closedBoxes";       
        pointer = document.getElementById(arrow).className = "arrowE";
        menuOpen = false;
    }
    },false);

This has solve the first problem but I am still stuck on how to make it so that only one box is ever open at one giving time. So when a user has signIn box open and clicks on currencyChanger I want the signIn box to be off.

http://jsfiddle.net/jamcoupe/kcF9Z/7/

Advertisement

Answer

When I click inside the element it closes, I only want it to close if they have clicked outside the box.

As I already said in my comment, if the box contains other elements, then e.target does not refer to the box itself but to the element within the box.

So in order to test whether the click was outside or not, you have to test whether e.target is an element within the box or the box itself. For that, you have to traverse the DOM tree.

Example:

var target = e.target;
while(target && target !== menu) {
    target = target.parentNode;
}

if(!target) {
   // click was outside of the box
}

You can open more than one of the showed elements when I only want one open at a time.

If you want to make the three dialogs dependent on each other, you have to maintain some shared state. I’d suggest, instead of having three dialogs, you can have one dialog manager which takes care of opening and closing the boxes.

Example:

function DialogManager() {
    this.dialogs_ = {};
    this.openedDialog_ = null;

    this.init_();
}

DialogManager.prototype.init_ = function(e) {
    var self = this;
    document.addEventListener('click', function(e) {
        var id = e.target.id;
        if(id && id in self.dialogs_) { // if one of the buttons was clicked.
            self.openDialog(id);        // the dialog is opened (or closed)
            return;
        }

        if(self.openedDialog_) { // if a dialog is currently open, we have to
            var target = e.target; // close it if the click was outside
            while(target && target.id !== self.openedDialog_) {
                target = target.parentNode;
            }
            if(!target) {
                self.closeDialog(self.openedDialog_);
            }
        }
    }, false);
};

DialogManager.prototype.registerDialog = function(button_id, dialog_id) {
    this.dialogs_[button_id] = dialog_id;
};

DialogManager.prototype.openDialog = function(id) {
    var open_id = this.openedDialog_;
    if(open_id) {
        this.closeDialog(open_id);
    }
    if(id !== open_id) {
        var dialog = document.getElementById(this.dialogs_[id]);
        dialog.style.display = "block";
        this.openedDialog_ = id;
    }
};

DialogManager.prototype.closeDialog = function(id) {
        var dialog = document.getElementById(this.dialogs_[id]);
        dialog.style.display = "none";
        this.openedDialog_ = null;
};

DEMO

I hope this gives you some idea. There is still a lot which can be improved, for example, now the manager listens to every click event, no matter whether a dialog is open or not.

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement