3

I don't have enough rep to post my own answer just yet, but here it is before I waste more peoples' time:

Ok, I now understand why I don't get all of the expected ancestors:

JQuery datepicker deletes the parent node (the datepicker-head) onclick. After that my event is triggered on the button of the element that already got deleted. So now I am "trapped" in the scope of the deleted element and can only traverse up to the deleted container itself. I think I can work out a solution for my event-delegation with this knowledge!

I will add this as the proper answer to my problem in 8 hours! ;)

The original question/problem: I've already thoroughly searched stackoverflow, google etc. for this problem and can't seem to find a solution.

I am going crazy over this problem: I have a JQuery UI Datepicker in a container that hides if you click outside of it. That means I need to know when there is interaction inside the container so it does not get closed! On document.click I am traversing up from the given element to find my enclosing container (or meet any other condition) to either allow the action, or deny it. This works like a charm - also clicks onto the generated datepicker work, except for the 'next month' and 'previous month' buttons. In the following fiddle I am logging the .parents() of the clicked element. Note that if you click the next and prev buttons the document.click will go through, closing the container and the log will only show the immediate parent of the buttons. If you click other elements in the datepicker you will see that the .parents() will give you all elements up to the enclosing html-tag.

Does anyone have any idea of how to fix this?

I made a simple example to showcase this... Here is the html:

<div class="container">
    <input type="text" class="myToggleInput" />
    <div class="toggleContainer" style="display: none">
        <div class="myDatepicker"></div>
    </div>
</div>

This is the script:

$(document).ready(
    init()
);

function init() {
    $('.myDatepicker').datepicker();
    $(document).on('click', '.myToggleInput', function(e) {
        $('.toggleContainer').show();
    });

    $(document).on('click', function(e) {
        //Logging the targets parents
        //Note that the next and prev buttons only go up one level in the DOM Tree, thus
        //not returning all of their parents...
        console.log($(e.target).parents());
        if ($(e.target).parents('.toggleContainer').length == 0 && !$(e.target).hasClass('myToggleInput')) {
            $('.toggleContainer').hide();
        }
    });
};

Here is the fiddle: http://jsfiddle.net/Px9Ab/

To clarify: Yes, I could add the classes of the next/prev buttons to my conditions, but I'd rather like a general solution, so I can be sure that '$(e.target).parents()' always traverses up to the html-node.

Why I can't use stoppropagation: There is a lot of dynamic stuff going on inside this container and the rest of the website. So for example I am reloading a list of elements dependent on the chosen date, which also have events attached to them, or rather delegate up to document as well. That's why I can't stop propagation on the container.

Any other solutions that completely circumvent my issue are welcome too! :)

Brian Tompsett - 汤莱恩
  • 5,195
  • 62
  • 50
  • 120
Daniel G.
  • 211
  • 2
  • 9

4 Answers4

4

JQuery datepicker deletes the parent node (the datepicker-head) onclick. After that my event is triggered on the button of the element that already got deleted. So now I am "trapped" in the scope of the deleted element and can only traverse up to the deleted container itself.

To circumvent my original problem I had to filter out all the clickable elements that do not bubble up.

Eg:

$(document).on('click', function(e) {
    if (!$(e.target).hasClass('ui-dialog')) {
        window.XYZ.hideAllDatePickersPseudoMethod();
    }
});

I know that's more some kind of a workaround than a fix for the problem, but it's the only solution I came up with that suits all my needs.

Daniel G.
  • 211
  • 2
  • 9
0

Just stop event propagation for any click events triggered on the .toggleContainer element:

$('.toggleContainer').on('click', function(e) {
   e.stopPropagation(); 
});

$(document).on('click', function(e) {
    $('.toggleContainer').hide();
});

jsFiddle demo

Anthony Grist
  • 37,428
  • 8
  • 62
  • 73
  • Can't do that. There is dynamic content which is loaded into that specific container. That content also has delegated events and can be inside this container or outside of it too. So I have to delegate these events from document too. :/ – Daniel G. Mar 14 '14 at 13:53
  • @DanielG. Using event delegation doesn't mean you have to use the document as the static element. If it's all inside of that container then just use the container as the static element for your event delegation. – Anthony Grist Mar 14 '14 at 13:57
  • Yes, that's true. Unfortunately, as I said, the list could be outside of the container aswell. As I am working on a rather complex project here, I want to keep the code as maintainable as possible. I don't want to delegate events from the same list to different containers, but rather have the content of these hideable containers all delegate their events to document. – Daniel G. Mar 14 '14 at 14:04
0

Use event.stopPropagation on input and .toggleContainer and datepicker navigational:

$('.myDatepicker').datepicker();

$(document).on("click", '.myToggleInput', function () {
    $('.toggleContainer').show();
});
$(document).on("click", function () {
   $('.toggleContainer').hide();
});
$(document).on("click", ".toggleContainer, .myToggleInput, .ui-datepicker-next, .ui-datepicker-prev", function (e) {
    e.stopPropagation();
});

http://jsfiddle.net/Px9Ab/5/

enapupe
  • 10,639
  • 3
  • 24
  • 41
  • I unfortunately can't do that. See my comment to Anthonys answer. – Daniel G. Mar 14 '14 at 13:58
  • Check my edit.. maybe this works for you. The issue is probably the fact that jqUI datepicker's is acting before our delegation, so, we specify the navigational selectors. – enapupe Mar 14 '14 at 14:03
  • Yeah, that would be the quick-and-dirty fix I mentioned in the op. ;) I can't to that either, because I don't know what elements will cause the same problem in the future ... I would have to add these to the list aswell. That's not really maintainable if there are possibly hundreds or thousands of elements that cause the same problem ... :( – Daniel G. Mar 14 '14 at 14:08
  • We may have to step back a little bit to find this solution, Why are you not using datepicker as its defaults, which should work just fine.. Why is this toggleContainer and stuff? – enapupe Mar 14 '14 at 14:25
  • The datepicker is just a part of a much more complex application. It's just the 'next' and 'previous' buttons of the datepicker itself that cause my described problem as you can see in the given example. All the other events work just fine. – Daniel G. Mar 14 '14 at 14:29
0
$('.myDatepicker').datepicker();

$(document).on("click", '.myToggleInput', function () {
    $('.toggleContainer').toggle();
});
$(document).on("click", function () {
   $('.toggleContainer').toggle();
});
$(document).on("click", ".toggleContainer, .myToggleInput, .ui-datepicker-next, .ui-   datepicker-prev", function (e) {
    e.stopPropagation();
});
Sathish
  • 31
  • 3