69

I'm looking for a DOM event that I can listen to with JavaScript for when a select element that has been opened (but no options changed) is then closed by clicking off the select element, somewhere (anywhere) else on the page.

It's not the blur event of the select, because the select retains focus. Likewise, it's not the focus event of some other element or the document, or a mousedown or click on the window, document or body.

It's not the change event of the select, since no option within the select has been changed.

I'm not concerned about legacy Internet Explorers - just something to work in standards compliant modern browsers. Proprietary hacks could be worth knowing though.

I've created a JSFiddle to demonstrate the problem: http://jsfiddle.net/premasagar/FpfnM/

  1. Click on the selectbox in the "Result" panel
  2. Click on the text marked "HERE" (or anywhere else) with a single click and see if any event is added to the log. There isn't an event in the latest Chrome or Firefox.

So the question is: What JavaScript could be added, to get an event logged when clicking off the selectbox?

(I've asked a similar, but different question here:
JavaScript on iOS: opening an HTML select element)

Community
  • 1
  • 1
Prem
  • 15,911
  • 11
  • 31
  • 35
  • 9
    If you explained why you wanted to do this, it might help. – Pointy Jun 01 '11 at 21:29
  • 2
    ... because the direct, simple answer to your question is, "no". :-) – Pointy Jun 01 '11 at 21:33
  • 1
    The reason why is in a comment on the other question: http://stackoverflow.com/questions/6097240/javascript-on-ios-opening-an-html-select-element#comment-7069149 But it's a convoluted reason. In a nutshell, I just want to get some programmatic information about the state of the select element when it changes. If it's not possible, I'm really curious as to why not(?) – Prem Jun 01 '11 at 21:37
  • Why? Well, finding a "why?" reason for the non-implementation of a feature is likely pretty hard. Browsers just don't do it. – Pointy Jun 01 '11 at 21:39
  • 3
    From reading that other question, it seems to me that you simply need to implement your own widget that more-or-less acts like a ` – Pointy Jun 01 '11 at 21:41
  • 3
    Fair enough, though it feels odd. The more I look into the behaviour of select elements, the more they feel to be a black box like a Flash embed. There's no way to programmatically open them, there's no way to check if they are open, there's no way to track the event of their closure. As web apps mature with HTML5 and all, there is greater control than ever on the components in the DOM, yet the humble select seems largely unscriptable. – Prem Jun 01 '11 at 21:44
  • Yes, I may need to implement my own version of a select. It'll be a shame though, because the web app is targetted at an iPad (but it'll work fine in a standard desktop browser). The iPad native – Prem Jun 01 '11 at 21:46
  • "get some programmatic information about the state of the select element when it changes" ... Then why not use onChange? As far as the other comments, remember that the web (specifically html in this case) is a stateless medium with just a bunch of hacks that try to hide that fact. – colinross Jun 15 '11 at 19:41
  • @colinross - when I said "changes", I meant that the select has been closed, whereas it was previously open. I didn't mean that the selected value had necessarily been changed. – Prem Jul 14 '11 at 14:38
  • The problem with triggering events on select box open or close is that it is possible to navigate to a select box using the keyboard, change the value, and navigate away without ever clicking on it, opening the drop list, or closing it. The only event that's guaranteed to be reliable is the change event. – Spudley Jul 14 '11 at 15:26

7 Answers7

40

Unfortunately there's no standard event for knowing when a select box is closed or open, so your code is going to be pretty hairy with accommodations for different browsers. That said, I think it can be done, and I've gotten something working for you in Firefox using the mouseup event:

http://jsfiddle.net/FpfnM/50/

In Firefox (and I'm assuming Chrome), you're going to need to track the state of that select pretty carefully. When an escape key is pressed or blur event occurs, you need to update the state to identify it as closed. I haven't implemented that in my demo, and you can see what happens if you hit escape instead of clicking off the select.

Things were easier in Safari, where a mousedown event on the select signifies opening the select, and any close of the select is signified by a click event.

If you find a browser where none of these events fire, you can try one additional trick. Because form widgets like select and textarea are often rendered by the OS and not inside the browser it's possible that you could interact with them and certain messages might not get down to the browser's event handler. If you were to position a transparent textarea covering the screen at a lower z-index when the select box is open, you might be able to use it to track that close click. It may be able to capture events that a browser DOM element may miss.

Update: Chrome sees a mousedown on the select when it opens and a mouseup on the document when the page is clicked with a select open. Here's a version that works with Chrome:

http://jsfiddle.net/FpfnM/51/

Again, you'll need to do some browser detection to handle the right behavior in each one. One catch with Chrome, in particular, is that no event is fired when you click on the select a second time to close it. You can detect the page click, however.

Quick Summary:

Chrome/Safari: select.mousedown on open, document.mouseup on close
Firefox: select.click on open, document.mouseup on close
IE8/IE7: select.click on open, document.mouseup on close

There are an awful lot of edge cases for other events that will signify a close (escape keypress, blur, etc.), but these are the minimum to handle the scenario where a user clicks the select box and then clicks off into the document area.

Hope this helps!

Jason Striegel
  • 1,043
  • 8
  • 9
  • Thanks. Yes, in Chrome I only get a logged event if the select's option has been changed. I don't get an event if I simply open the select and then it close by clicking "HERE". – Prem Jul 14 '11 at 14:46
  • @Premasager - I've updated the answer with a Chrome example and tested the behavior on IE as well. IE/Firefox seem to work similarly for both the open and close handler. Chrome/Safari need to watch for mousedown on the select when it's opened, and mouseup on the document for it's close. – Jason Striegel Jul 14 '11 at 15:14
  • 2
    That jsFiddle only toggles the "open/closed" message, and does not accurately reflect the actual event. (Example, just click and select an item, then do it again.) – mix3d Sep 30 '15 at 20:34
  • In my case I catch the close event adding a listener when mouse leaves, like this: `document.getElementById('selectId').addEventListener('mouseleave', ($event) => { console.log($event); });` – Victor Oliveira Sep 24 '19 at 10:55
  • This doesn't work on iOS Safari. Both `mousedown` and `mouseup` fire when it's opened, but neither fires when it's closed. I tried `click`, `blur`, and others to capture the close, but none are firing. – Alex Jan 19 '22 at 11:28
2

When you have more then 1 dropdown:

        $("select").each(function () {
        var initDropdown = $(this);
        initDropdown.data("open", false);
        console.log(this.name + "   closed");

        initDropdown.on("blur", function () {
            var blurDdopdown = $(this);
            blurDdopdown.data("open", false);
            console.log(this.name + "   closed");
        });
    });

    $("select").bind("click", function () {
        var clickedDropdown = $(this);

        if (clickedDropdown.data('open') == false) {
            clickedDropdown.data('open', true);
            console.log(this.name + "   open");
        } else {
            clickedDropdown.data('open', false);
            console.log(this.name + "   closed");
        }
    });
Bogdan
  • 21
  • 2
2

If we consider clicking outside the selection box can be a signal of the ending selection event.

The following jQuery can do this. Here is code in fiddle

$(function () {
    let flag = 0;

    $("#selectId").click(function (e) {
        e.stopPropagation();
        if (flag === 0) {
            flag = 1;
            $(document).one("click", function () {
                flag = 0;
                alert("select end");
            });
        }
    });
});

Html code:

<select multiple id="selectId">
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="opel">Opel</option>
<option value="audi">Audi</option>

Marshall Z
  • 21
  • 3
1

I got the following events by following the directions on your JSFiddle to the letter:

BODY, mousedown, STRONG
#document, mousedown, STRONG
window, mousedown, STRONG
SELECT, blur, SELECT
BODY, click, STRONG
#document, click, STRONG
window, click, STRONG

These are all events that were triggered when I clicked "HERE" after the select menu was already in focus and expanded. This was in the latest version of Chrome.

Shouldn't any one of these suffice for your purposes?

Edit: If you want to make sure it's your Select element that's losing focus, set a global Javascript variable, 'selectFocused', and set it to False. Set it to True when your Select menu receives focus, and set it to False when any of the above events occurs. 'selectFocused' can now be used anywhere in your code to detect whether or not your Select element currently has focus, and when it changes values, you know your Select element has been selected or unselected.

sichinumi
  • 1,835
  • 3
  • 21
  • 40
  • Did you click "HERE" just once? Or twice? In the latest Chrome, the – Prem Jun 01 '11 at 22:03
  • I also see this behavior: Chrome vs. 13.0.772.0 dev-m – squidbe Jun 01 '11 at 22:17
  • Hmm. I'm also on Chrome 13.0.772.0 dev (on Linux). I don't get the blur event there when I click off just once, or in Firefox 4.0.1. – Prem Jun 01 '11 at 22:23
  • On Firefox 4.0.1 on Windows, it takes a second click on HERE before these events show up. No events are fired on the first HERE click. – Kevin Jun 01 '11 at 23:41
  • Iceman, bogey is confirmed. I get nothing out of Firefox, that output was from Chrome (although I only clicked once, Premasagar). OP, this seems like something you should submit to bugzilla, as Chrome and IE both behave like I stated above, only Firefox does not register any events. Not sure why Chrome in Linux isn't behaving the same, though. :( – sichinumi Jun 02 '11 at 13:29
1

My first instinct is a little roundabout in its way to achieve this, it would be to use the code you normally use for closing a (custom) dropdown menu on press outside:

function clickInBody(){
 functionToCallOnBlur();
}

function clickInBodyStop(event){
    event.stopPropagation();
}

Then on your body tag you add onClick="clickInBody()" and on your select item you add onClick="clickInBodyStop(event)". This should call the event every time you click on the page, but if you click on the select tag it will stop the propagation and not call functionToCallOnBlur()

Tomas Reimers
  • 3,234
  • 4
  • 24
  • 37
  • But the problem is that, as shown by the JSFiddle snippet, there is no `click` event fired on the body when the user single-clicks off the open select. – Prem Jun 03 '11 at 04:26
  • I think I found the problem, as shown by [this](http://jsfiddle.net/3PYyV/10/) jsfiddle and firebug, the problem is that `clickInBody` is not defined, so this is a problem with jsfiddle and not the code, it *should* work locally... – Tomas Reimers Jun 03 '11 at 11:26
  • As I say, the solution you are proposing cannot work if the body element never fires a `click` event, which it doesn't ever appear to do. – Prem Jun 06 '11 at 06:26
  • 1
    then put a wrapper div around the entire body and have that fire the `onclick` event. – Tomas Reimers Jun 06 '11 at 22:07
  • I don't understand. Put a div around the body? There are no click events produced on any elements, so I don't see how this can solve the problem. – Prem Jun 10 '11 at 16:41
  • Ok, so sorry for making you wait so long for this result, but I finally solved it. [This](http://jsfiddle.net/RAHfb/4/) jsfiddle shows a working prototype using the method I originally discussed. Note that the body tag is in the info part of the right sidebar in jsfiddle (under the info section) and is coded as such: `` – Tomas Reimers Jun 12 '11 at 05:20
  • Thanks, Tomas. In Chrome, the alert is triggered whenever I change the select option and then click off, but it isn't triggered when I just click the select without changing the option and then click off. – Prem Jun 13 '11 at 16:48
  • Same in Firefox. I'm going to go ahead and say that this doesn't exist because browsers all handle dropdown menus sooooo differently (try the same simple css of changing font colors for dropdown menus on two different browsers). My best advice would be to create a hidden field which contains the value to submit and determine that value based on a custom dropdown menu... – Tomas Reimers Jun 13 '11 at 18:14
1

precondition: using jQuery! This will probably only solve part of your problem, but if you are looking for an event to fire when you click off an element, you can use an overlay div.

When the user clicks on the select box:

$('#mySelectBox').click(function () {
    var myOverlayDiv = $('<div id="overlayDiv" class="overlayDiv"></div>')
    .click(function () {
        // call your "select lost focus" function here
        // which should include $('#overlayDiv').remove() somewhere!
    })
    .show()
    .appendTo(document.body);
});

Style for the overlay div:

.overlayDiv {
position:absolute;
top:0;
left:0;
width:100%;
height:100%;
opacity:0;
z-index:1000;
}

You need to make sure that your select box menu is a higher Z-index so when the user clicks on the menu they get the menu and not the overlay div :)

jbabey
  • 45,965
  • 12
  • 71
  • 94
  • You can also add some simple code to this function and the "select lost focus" function to compare the values of the select before and after it was interacted with by the user. This will allow you to see if the value was changed or if the box was only opened and closed. – jbabey Jul 08 '11 at 13:28
  • I'm pretty sure this won't work. The element doesn't received a "click" event when the select is closed by single-clicking off the select box, so I can't quite believe that a div element will. Have you tried that and got it working. I'll try it out. – Prem Jul 14 '11 at 14:41
  • Yes, I have it working in a web app that uses some custom drop down menus. – jbabey Jul 15 '11 at 18:29
  • I forgot one thing ( both in my app and in the answer ) - when the overlay div receives a click event, make sure you remove it from the DOM so you don't keep adding them every time :P – jbabey Jul 15 '11 at 18:32
1

The answer is "YES", there is such an event and that is called blur event. It's very general and depends on the html element.

In the case of an HTMLSelectElement, blur is called when you exit focused state on the element, meaning you've closed it.

And the same can be said with focus that it can be assumed as a substitute for an 'open' state of the HTMLSelectElement element.

Here's an example on doing something callback when you close the HTMLSelectElementelement:

var select= document.getElementById("mySelect");

function doSomethingOnBlur(event){
  console.log("Yey! The select options were closed.");
}

select.addEventListener("blur", doSomethingOnBlur);
<select id="mySelect">
  <option selected hidden></option>
  <option>Some Item</option>
</select>

It's important to note that the options are rendered based on the state of the select. When you focus on it, it opens. And when you lose focus (blur) then it's closed. So technically, it's just a choice of words...

Mosia Thabo
  • 4,009
  • 1
  • 14
  • 24