Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at CFinNC 2009 (Raleigh, North Carolina) with:

FLEX On jQuery: Handling Mouse-Down-Outside Events

By Ben Nadel on

A little while ago, Ben Alman released a jQuery plugin that created special jQuery event types for "outside" user events. This plugin allows you to listen for events, such as clicks, that happen outside of a given element. Ben's plugin is rather awesome and it seemed like a piece of functionality that would be great to explore from a FLEX point of view. Over the weekend, I had a chance to chat with FLEX developer and podcaster, Jeffry Houser, (chat reproduced below) about how a FLEX application might track "outside" events.

 
 
 
 
 
 
 
 
 
 

As it turns out, the FLEX approach and the jQuery approach are quite similar. Of course, since both frameworks are very much event-driven, I suppose this is not all that surprising. In either case, the component that needs to listen for the "outside" event simply binds an event listener to itself for the given event (ex. mouseDownOutside); the component doesn't worry about how that event is managed - it simply worries about listening for the event. All the event wiring is done by the framework itself, unbeknownst to the component. In a FLEX application, this wiring is done by the Flash platform; in a jQuery application, this wiring is done by the jQuery framework (as beautifully demonstrated in Ben Alman's special-event plugin).

The biggest difference between the two contexts is that a FLEX application primarily deals with components, where as a jQuery application primarily deals with DOM elements. While this difference might not seem too big from a conceptual standpoint, it is quickly becoming one of the murkiest aspects of my FLEX On jQuery exploration. As I try to transfer concepts from one platform to another, what I'm ending up with is a mish-mash of event binding styles; sometimes I bind to DOM elements and sometimes I bind to objects.

When I'm internal to a component / object, I think it's fine to bind to standard UI / DOM elements as these are part of the component's implementation; as such, the component will need to listen to local core events as it translates them into more meaningful application events. But, when I'm external to a component, I believe I want to be triggering all events on the target component and not to any part of its UI as this would highly couple the program to the component's implementation. I think this sounds really good theoretically; but, when it comes to implementing something like Ben Alman's "outside" events plugin, it becomes much more complicated.

Ben's plugin only understands DOM elements and their relative position within the DOM tree. As such, when you go to wire up an "outside" event, you have to bind it to a DOM element. The problem with this, when dealing with components, is that you end up having the external framework triggering events on a UI element - part of the component's implementation - and not on the parent component itself. To get around this, you either have to wire the events more manually (as I've done in the demo below); or, you have to listen for events internally and then re-announce them at the component level. While the former seems more correct some how, the latter is just way more practical in jQuery.

Here is a demo of how you might wire this up manually:

  • <!DOCTYPE HTML>
  • <html>
  • <head>
  • <title>FLEX On jQuery: Handling Mouse-Down-Outside Events</title>
  • <style type="text/css">
  •  
  • #photo {
  • border: 1px solid #666666 ;
  • display: block ;
  • margin: 0px 0px 1.5em 0px ;
  • }
  •  
  • </style>
  • <script type="text/javascript" src="../jquery-1.4.2.js"></script>
  • <script type="text/javascript">
  •  
  • // I am the component for the photo UI.
  • function PhotoController( target ){
  • var self = this;
  •  
  • // Store a reference to the UI aspect of the component.
  • this.ui = target;
  •  
  • // Bind to the click event locally.
  • this.ui.click(
  • function( event ){
  • // Simply log to the console that this event
  • // has taken place.
  • console.log( "Click Inside" );
  • }
  • );
  •  
  • // Bind to the click event outside. This will be wired
  • // up by external factors; but, we will listen for it
  • // internally to the component.
  • $( this ).bind(
  • "mouseDownOutside",
  • function( event ){
  • // Simply log to the console that this event
  • // has taken place.
  • console.log( "Click Outside" );
  • }
  • );
  •  
  • };
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // When the DOM is ready, initialize the scripts.
  • $(function( $ ){
  •  
  • // Create an instance of the photo controller.
  • var photoController = new PhotoController(
  • $( "#photo" )
  • );
  •  
  • // Keep an array of components that are going to be
  • // tracking click-outside events (NOTE: we will be
  • // able to grab the UI aspects of the components from
  • // their UI property).
  • var popups = [ photoController ];
  •  
  •  
  • // Bind the mousedown event to the document so we can
  • // track all clicks made by the user.
  • $( document ).mousedown(
  • function( event ){
  •  
  • // Loop over the components that are being
  • // tracked as popups.
  • $.each(
  • popups,
  • function( index, controller ){
  •  
  • // Check to see if the click event
  • // happened outside of the given
  • // component.
  • if (
  • (event.target != controller.ui[ 0 ]) &&
  • !$( controller.ui[ 0 ] ).has( event.target ).size()
  • ){
  •  
  • // This click event did not take
  • // place inside of the given UI
  • // aspect. Trigger the outside event
  • // on the component.
  • $( controller ).trigger({
  • type: "mouseDownOutside",
  • relatedObject: event.target,
  • pageX: event.pageX,
  • pageY: event.pageY
  • });
  •  
  • }
  •  
  • }
  • );
  •  
  • }
  • );
  •  
  • });
  •  
  • </script>
  • </head>
  • <body>
  •  
  • <h1>
  • FLEX On jQuery: Handling Mouse-Down-Outside Events
  • </h1>
  •  
  • <img
  • id="photo"
  • src="sexy.jpg"
  • width="500"
  • height="333"
  • alt="Sexy girl drinking all naughty-like."
  • />
  •  
  • <p>
  • Photo:
  • http://www.flickr.com/photos/cdrewing/166568783/
  • </p>
  •  
  • </body>
  • </html>

In this demo, I am triggering the "mouseDownOutside" event on a component rather than a UI element; but, as you can see, in order to do that, I still need to mess around with the component's UI aspect. Even when I try to be more component-oriented, there is still a necessary mish-mash of event bindings.

In FLEX, there doesn't appear to be much of a difference between a component and the UI that it composes. In jQuery, on the other hand, there is still a huge separation between the UI and any code that coincidentally controls it. As such, I'm beginning to accept the reality that event binding in a thick jQuery client is going to necessarily be a combination of component-based and UI-based event binding. The sweet spot, I suppose, will be where the ease of DOM-based implementation meets the meaningful API provided by the parent components.

Taking that mindset, I reworked the above demo to use Ben Alman's "outside" event plugin:

  • <!DOCTYPE HTML>
  • <html>
  • <head>
  • <title>FLEX On jQuery: Handling Mouse-Down-Outside Events</title>
  • <style type="text/css">
  •  
  • #photo {
  • border: 1px solid #666666 ;
  • display: block ;
  • margin: 0px 0px 1.5em 0px ;
  • }
  •  
  • </style>
  • <script type="text/javascript" src="../jquery-1.4.2.js"></script>
  • <script type="text/javascript" src="jquery.ba-outside-events.js"></script>
  • <script type="text/javascript">
  •  
  • // I am the component for the photo UI.
  • function PhotoController( target ){
  • var self = this;
  •  
  • // Store a reference to the UI aspect of the component.
  • this.ui = target;
  •  
  • // Bind to the click event locally.
  • this.ui.click(
  • function( event ){
  • // Simply log to the console that this event
  • // has taken place.
  • console.log( "Click Inside" );
  • }
  • );
  •  
  • // Bind to the click event outside. This will be wired
  • // up by external factors; but, we will listen for it
  • // internally to the component UI.
  • this.ui.bind(
  • "mousedownoutside",
  • function( event ){
  • // Simply log to the console that this event
  • // has taken place.
  • console.log( "Click Outside" );
  • }
  • );
  •  
  • };
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // When the DOM is ready, initialize the scripts.
  • $(function( $ ){
  •  
  • // Create an instance of the photo controller.
  • var photoController = new PhotoController(
  • $( "#photo" )
  • );
  •  
  • });
  •  
  • </script>
  • </head>
  • <body>
  •  
  • <h1>
  • FLEX On jQuery: Handling Mouse-Down-Outside Events
  • </h1>
  •  
  • <img
  • id="photo"
  • src="sexy.jpg"
  • width="500"
  • height="333"
  • alt="Sexy girl drinking all naughty-like."
  • />
  •  
  • <p>
  • Photo:
  • http://www.flickr.com/photos/cdrewing/166568783/
  • </p>
  •  
  • </body>
  • </html>

In this approach, you can see that the code is much more compact. I'm relying on the jQuery framework and the "outside" event plugin to manage the "mousedownoutside" event; however, in order to do so, I am allowing the jQuery framework to interact with part of the component's UI implementation. While at first I worried that this would create high-coupling, because the event is bound by the component itself, I think we avoid any real coupling between the application and the component implementation.

In an environment like jQuery, where there is a hard distinction between the DOM and the code that manages it, it seems that we have to embrace the fact that so much interaction is DOM-based. Even when we want to take a more component-focused approach as influenced by the FLEX framework, it's quite advantageous to build on top jQuery's powerful DOM manipulation rather than trying to find ways to work around it. It is in this sweet spot where I suspect we will get the most mutually beneficial outcome.

Here is the chat that I had with Jeffry Houser:

Jeffry Houser: So, the question, if I understand it; was how does a Flex component listen for events not originated by that component. Is that correct?
BenNadel: yeah. Basically, is there a "Best practice" way for a component to track events that bubble up to the stage, without passing through the given component?
BenNadel: or, are all those types of things wired up directly by the App / children?
BenNadel: (forgive any misuse of terminology - I'm very new to this)
Jeffry Houser: Hopefully this answer doesn't sound too beginner for you. :-)
But, I'd say that there are two aspects to Flex Events. The 'listener' component and the 'dispatcher' component. Sometimes those components can be the same one, but not always.
BenNadel: right
Jeffry Houser: So, if a component dispatches an event; it can listen to. Or it's parent can listen to it [assuming the event does not bubble].
BenNadel: Let me give a quick scenario
Jeffry Houser: Go for it!
BenNadel: imagine you have a little date-picker popup off an input (for argument sake). Let's say this date-picker is a component
BenNadel: the desired behavior is that if someone clicks anywhere outside of the date-picker, the date-picker will close
BenNadel: what would be the best approach for that date-picker to listen for that click?
Jeffry Houser: With Flex, there are some events that will fire in the component when the mouse moves outside of it and/or clicks outside of it.
Jeffry Houser: So, probably within the datePicker component, I might listen to mouseDownOutside
BenNadel: these are inherent to the Flex Framework?
Jeffry Houser: Yep
Jeffry Houser: http://livedocs.adobe.com/flex/3/langref/mx/core/UIComponent.html#event:mouseDownOutside
BenNadel: very interesting!
BenNadel: hmmmm
Jeffry Houser: It looks like that one is specific to components opened w/ the PopUpManager.
Jeffry Houser: So, you probably want to listen to the focus_out event.
Jeffry Houser: Which would give you something that works more consistently.
BenNadel: I gotcha - so there's something that would manage this on specific types of components - not just any UI component
Jeffry Houser: Info on Focus Out: http://livedocs.adobe.com/flex/3/langref/flash/display/InteractiveObject.html#event:focusOut which looks like it is part of Flash Player [not the Flex Framework]
BenNadel: so, if someone was to build a component that took advantage of this event, they would bind an event listener internally...ex (this.addEventListener( "mouseDownOutside" ) ... psuedo code)?
Jeffry Houser: Correct!
BenNadel: ahhh, very interesting
Jeffry Houser: As part of the event data; you have access to the object which the user clicked over; which is a property named relatedObject .
Jeffry Houser: I only have about 2 days of JQuery experience, so can't compare and contrast.
BenNadel: right, the jQuery event object has a similar proprety. I think also called relatedObject
BenNadel: typically used for mouse out/over events.
BenNadel: Jeff, thanks a lot - this is all very intreesting
Jeffry Houser: Glad to help! :-)
BenNadel: So, just to sum this up, the component itself doesn't worry about managing the events - it just listens to itself for any triggering of the outside event.... the wiring up of events are all managed by the application / pop-up manager.
Jeffry Houser: Correct.
BenNadel: ok cool.
Jeffry Houser: I'm sure it's complicated stuff going on under the hood; but from the perspective of me as a developer it just works.
BenNadel: yeah, I think that's the best way ;) offload the heavy lifting to the framework / flash.

Most of this is just me thinking out loud; so, if you have something constructive to add, I'd love to hear it.




Reader Comments

A few comments:

First, you said:
"In FLEX, there doesn't appear to be much of a difference between a component and the UI that it composes."

For all intents and purposes, Flex is a UI Framework. In the Flex 2/3 Halo component architecture, it is very common that display and 'business logic' are mixed together.

The Flex 4 Spark Component architecture changes that drastically and it is much easier to create a finer separation between UI and business logic in a single component. This is done primarily for skinning purposes.

Second, Flex uses a class named FocusManager to handle focus events. I would suspect that it the FocusManager that triggers a focus out event on the component which lost focus.

Reply to this Comment

@Jeffry,

How do events actually bubble in a FLEX application? This is one of the implementation details that I have no footing on. Do events bubble up through the UI elements? Or through the components themselves?

In Javascript, the events bubble up through the DOM elements; if we want to wrap component around those, we have to explicitly intercept events bubbling up through the DOM. Is this how FLEX works? Or is it all component-oriented?

It's this point - of event propagation - that makes the UI and components so tightly coupled in FLEX (but this could just be me not understanding anything about FLEX).

Of course, there are also different levels of components; I think this is perhaps the real difference. In FLEX, the Button is a component and in Javascript, the button is viewed as just another DOM element.

I guess at some level, DOM elements are parallel to components. So, perhaps thinking about core components are not meaningful.

Sorry - I know that's not entirely coherent. I'm just thinking out loud. here.

Reply to this Comment

@Ben Nadel,

Some events bubble, some don't. It is completely optional and can be controlled when creating the event. There is a "bubbles" property of the default event object.

If they bubble; the go up the hierarchy chain; being dispatched from the component, then it's parent, then it's parent parent, etc..

Any point along the line; an event handler may terminate the event's bubble phase; I believe using a method called stopImmediatePropogation(); which will prevent any more listener methods from executing.

I believe, for the level of understanding you are looking to obtain, MXML can be used to mask how event listeners are actually working.

I would view an MXML documents as I would HTML documents. An HTML select is like a Flex ComboBox. And they can be wrapped in some container (Such as A DIV in HTML or a Canvas in Flex). Both the container, and the UI Component can be modified by other code in the same "file."

To the extent of my knowledge, there is no way to encapsulate code or create your own components in HTML; but that is what most Flex developers do. [Does JQuery provide a mechanism to encapsulate HTML elements into reusable components?].

Does this help?

Reply to this Comment

@Jeffry,

In HTML, if I trigger an event on a SPAN, for instance, the event bubbles up implicitly through its ancestor chain unless stopped explicitly. If, however, I trigger an event on an object such as a Javascript class instance, there is nothing that implicitly bubbles that event up to anything.

In FLEX, do components have an implicitly wired hierarchy in *addition* to the MXML? For example, let's say I am some WYSIWYG editor that can announce a "pasteEnd" event (for argument's sake). Would that pasteEnd event bubble up implicitly to the DisplayList that contains it?

Reply to this Comment

@Ben,
If bubbles=true in the event that is being dispatched, then it will bubble up through the ancestor chain without any interaction from your code.

So if your main Application "page" (for some reason) wanted to listen for that event to be propagated to it, you would set bubbles=true, dispatch that event, and add your listener in the Application "page". This would keep going through its ancestors or until something stopped it along the way (what Jeff described with event.stopImmediatePropagation() ).

Reply to this Comment

@Ben,

In Flex, I would say that events bubble up implicitly through the ancestor chain; but only if the 'bubbles' property on the event object is set to true. If the bubble property is set to false, then events only go up to the component's parent.

It is unusual for non-UI components to dispatch events, although it is possible if you extend the EventDispatcher class.

In the Flextras Calendar Component, for example, I have a manager class to handle sharing the dataProvider between multiple days across the week view, month view, and day view. I the dataProvder changes, an event is fired from this class. Since the "MonthDisplay", "DayDisplay", and "WeekDisplay" all have a link to the reference of the class, they all are listening for this change event and all run a method to update their view accordingly.

But, the event--a custom one I created--is not set to bubble; so I have not tested how bubbling works on non-UI Components. IT would be my expectation that the event would continue up the chain.

It is my perception that the HTML page DOM is a more complex 'hierarchy' than what you'd find in a Flex Application. This is because HTML Documents cannot be separated into multiple files the same way that a Flex can.

I hope this helps. ;)

Reply to this Comment

@Gareth, @Jeffry,

Thanks, this is helpful. I guess I'm just having a bit of mental hurdle somewhere in how I am visualizing this stuff. But, hopefully all of this exploration will start to make everything a bit more clear.

I think what I'm starting to formulate though, is that it is reasonable to allow all DOM-based events to be wired with jQuery, even if that requires an outside reference (ie. jQuery) to have access to a component's implementation (ie. a DOM element "within" the UI aspect of a Javascript class). While this feels a tiny bit like breaking implementation hiding, maybe that's just me stressing too much.

Reply to this Comment

@Ben,

Of course, adding an outside dependency to a component is always undesirable. But, it all depends on the purpose and goals.

When I build components for Flextras; I have a dependencies to The Flex Framework. But, it is okay because I assume that all developers interested in my components will also be using The Flex Framework.

In my experience w/ CF Frameworks; they seem to encourage that you tie your views--and in some cases CFCs--to the framework by accessing Framework APIs directly. This limits reuse to other projects that also use the framework.

It does not surprise me that a AJAX framework would introduce a similar limitation. We just have to think twice to decide if the trade off is worth it, or not.

Reply to this Comment

"FLEX on jQuery??????" Are we hard up for a topic? May I suggest "Implementing ActionScript event models on CP/M based Ford Electronic Ignition System processors?"

Reply to this Comment

@Occam,

Despite the fact that this post is over 3-years old, I am not sure what it is problematic. FLEX apps were sufficiently complex that I thought it would be beneficial to talk to people who created them. Do you disagree?

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.