4

I'm trying to set a global event binder on $(document) which roots to all of my core application functions.

I'm using some jQueryUI widgets (from jQuery Mobile) and was hoping I could also include the widget events in my global handler.

So I'm trying to do this:

 $(document).on("click change filterablebeforefilter", ".action_elements", function (e) {
   // foo
 });

However, this does not work. I can set click and change listeners on my widget input event, but the filterablebeforefilter cannot be set like this. At least I cannot get it to work.

The JQM demos specify only a direct binding:

 $( ".selector" ).on( "filterablebeforefilter", function( e, data ) {
  // foo
 });

Question:
but I'm wondering if there is no way to attach the event to document?

Thanks!

frequent
  • 27,643
  • 59
  • 181
  • 333
  • you always come up with tricky questions ;) – Omar Oct 18 '13 at 15:31
  • 1
    I think I also have an answer... I assume jQueryUI widget events are not the same as "browser events", so they stay within their "realm" and don't propagate or bubble anywhere, so setting a listener on document will not work. Still need to make sure – frequent Oct 18 '13 at 16:03
  • Nice question, and your explanation sounds reasonable. You should add an answer for this so it doesn't show up still as unanswered. Additionally, if you do want to hang a listener up at that high a level (and this wasn't just a quick example), you should probably specify the filtering elements a bit better - just a single class like '.action_elements' can give poor performance depending on your document structure - it would be better to specify the element type too, such as 'div.action_elements'. – John - Not A Number Jan 24 '14 at 22:11
  • @John-NotANumber - I eventually went with `$(document).on("click change keyup input", ".action", function (e) {...}` It is high up and quite "generic" but it is the only binding I'm setting in my application (for link, inputs and selects). Have not tested if this is more performant than individual bindings, but it sure is easier to maintain :-) – frequent Jan 25 '14 at 07:30

1 Answers1

0

Custom Event Handlers on $(document) do work:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>OnEventHandler</title>
    <script src="Scripts/jquery-2.1.0.min.js"></script>
    <script language="javascript" type="text/javascript">
        $(document).ready(function () {
            $(document).on('click filterEvent', '.action_element', function () {
                alert('You Clicked Me!');
            });
            $('#OrderFilter').change(function () {
                $('#CustomerFilter').trigger('filterEvent');
            });
            $('#CustomerFilter').on('filterEvent', function () {
                alert('CustomerFilter filterEvent');
            });
        });
    </script>
</head>
<body>
    <p>Some text.</p>
    <p class="action_element">Click me!</p>
    <select id="OrderFilter">
        <option>By Order Id</option>
        <option>By Order Total</option>
    </select>
    <p id="CustomerFilter" class="action_element">Just some Text...</p>
</body>
</html>

You can delegate a handler for a custom event using $(document).on('event', 'selector').

EDIT

So, that rules out anything fishy with custom events propagating up to the document level and being handled by a delegated event handler there.

Looking at the JQM filter and your question, are you asking: if they click in the input box or the input box changes, or the widget is right before it starts applying filtering to some data, then do function(){...}?

If the intent is to override the filtering process to get some data to filter on, before filtering is done, as the documentation puts it; from the database first (Google search comes to mind...) wouldn't it make sense that it applies to the widget at hand? If you had more than one widget in the app, would you want all of them to have the same DB call for data (say 4 google search input boxes on the same page?)

It would make sense that beforefilter event would have to be unique for each widget (think 3 text boxes, one for google, one for yahoo, one for bing). If so, How they accomplish this I don't know yet (but will try as I am interested in knowing...)

Also, there is a 250ms delay before the filter kicks in, so it waits to see if the user is done typing... With the change event being delegated as well, the handler would execute for it, and be done or on the way to being done 250ms later when the beforefilter event is fired... so the handler gets executed twice? Whether it does or does not, does it make sense?

I'm guessing, they have it so it can only have a beforefilter event handler per widget, by design as explained above. Also note in the documentation example, they are using the direct binding notation of .on() (no selector specified). Everything points to a binding to an element, rather than delegation.

  • `$(document).on(...)` will _only_ attach event handlers to the `document`. `$().delegate` is just an alias of `$().on` with its arguments reordered. – John Dvorak Jan 26 '14 at 07:41
  • "The closer you can get your handler to the '.action_elements', the less traversing up the DOM jQuery has to do, which translates to less processing and better performance." - that's not true either. jQuery has to traverse to the top in _any_ case, if only to verify there is nothing to do there. In any case, the effect is negligible. Traversing up is fast because an element only has so many parents. – John Dvorak Jan 26 '14 at 07:43
  • .on() can be directly bound (if the selector is omitted or null), or in a delegated fashion if a selector is specified. I prefer the use of .delegate() as the intention is clear. Either way, delegating (using .on() or .delegate()) to the closest parent in the DOM of the triggered elements will result in fewer bubble up steps, to the handler, and attaches fewer handlers. Please see the section 'Direct and delegated events' in the [jQuery documentation](http://api.jquery.com/on/) – ScryptKeeper Jan 26 '14 at 08:08
  • "delegating ... to the closest parent ... attaches fewer handlers" -- definitely not. I know for sure that `$(document).on` always attaches exactly the event handler to exactly one element, never more than one. On the other hand, if the parent selector chooses more than one element, the same handler gets attached to more than one. Granted, it's still better to attach as close as possible unless you have hundreds of elements. – John Dvorak Jan 26 '14 at 08:16
  • @JanDvorak : Please read the text around the code example of the 'Direct and delegated events' of the [.on() jQuery Documentation](http://api.jquery.com/on/) If you were to $( "#dataTable tbody tr" ).on( "click", function() {...} on a table with a 1000 rows, 1000 handlers would get attached. However, using .delegate() or .on() as follows: $( "#dataTable tbody" ).on( "click", "tr", function() {...} "attaches an event handler to only one element, the tbody, and the event only needs to bubble up one level (from the clicked tr to tbody)" (citing the jQuery documentation). – ScryptKeeper Jan 26 '14 at 09:14
  • You keep assuming that the asker uses `on` as `bind`. `on` is meant as a replacement for both `bind` and `delegate`, and the asker clearly wants to use it as a replacement for `delegate`. You keep assuming `on` = `bind`. The asker understands the difference between direct binding and event delegation, and so do I. He asks if certain events can be bound using delegation (no matter what syntax). You don't answer that. – John Dvorak Jan 26 '14 at 09:21
  • concerning your edit - `document` exists even before the document is fully loaded. Why do you suggest waiting for `$(document).ready`? The point of event delegation is to avoid having to wait for the target elements to exist. – John Dvorak Jan 26 '14 at 09:23
  • (though I do acknowledge of the debugging efforts you're making, I can't consider it a definite answer before it's confirmed as the real cause. I hope you don't mind.) – John Dvorak Jan 26 '14 at 09:28
  • He asking why he cannot handle the custom event the widget is firing by delegating the handler to the $(document). Notice standard fare browser events such as click and change work, but not the custom one. Take the code sample provided. Run it as is. It works, right? Now, take away $(document).ready(function(){})... Run the code without the ready... The click is handled but the custom event is not. – ScryptKeeper Jan 26 '14 at 09:31
  • Did you move the `trigger` outside `$(document).ready` as well? If you did, there would be no element to trigger the event upon, and thus no event to listen for. Somehow I don't think this is what happens here. – John Dvorak Jan 26 '14 at 09:36