3 Replies Latest reply on Aug 19, 2014 5:42 PM by RobertKyle

    Confused by stopPropagation()

    RobertKyle Level 2

      Hoping, first, that I'm just making a silly mistake or, if not, to better understand what it means to stopPropagation() on an event.

       

      Consider this snippet:

       

      var beforeCloseListenerTest = app.addEventListener("beforeClose", beforeCloseListenerTest);

       

      function beforeCloseListenerTest(beforeCloseEvent) {

          if (beforeCloseEvent.target.constructor.name == "LayoutWindow") { // first beforeClose event

              alert("Layout Window is closing. Propagation will be stopped.");

              beforeCloseEvent.stopPropagation();

          } else {

              alert(beforeCloseEvent.target.constructor.name + " is closing.");

          } // end if

      } // end function

       

      So, when this event listener is running and a document is closed, the alert is displayed saying that the Layout Window is closing. As expected. But the second alert also displays when the Document closes. My brain is stuck on the idea that stopPropagation() should prevent that.

       

      The real event-listener handlers that I'm using all run before the Layout Window closes. It's certainly not a big deal to check the target.constructor,name before deciding whether to do something, but I'm thinking that these scripts would be slightly more efficient if they just stopped at that point.

        • 1. Re: Confused by stopPropagation()
          Trevorׅ Adobe Community Professional

          Hi Robert

           

          In short you have to either use a flag or check the target. stopPropagation() will not work even if if should

           

          Trevor

          • 2. Re: Confused by stopPropagation()
            Marc Autret Level 4

            Hi Robert,

             

            I wouldn't say Event.stopPropagation() does not work, it simply doesn't operate the way you expect it to. In fact, there are very few cases where stopping the propagation of an event is relevant in the InDesign DOM event field, because most of the time we only manage an event from a single listener and through a single event handler. That is, our code usually manages a single propagation step and there is no further stage to be stopped at all.

             

            What is confusing in your code is that two distinct events are actually considered, each having its own propagation process. The first event that occurs—say ev1—is a beforeClose on the LayoutWindow target, then a distinct beforeClose event occurs—ev2—on the Document target.

             

            Stopping event propagation has sense only if some event has multiple registered listeners and/or handlers that may manage the event instance during its BUBBLING phase, that is, from the AT_TARGET step to the very last listener in the hierarchy. Let's consider ev1 (that is, the LayoutWindow beforeClose event). It occurs at the LayoutWindow target, then it 'bubbles' to the Document level (since LayoutWindow.parent is a doc) and finally to the Application level. Hence there are three propagation steps for that event. As for ev2 (that is, the Document beforeClose event), it occurs separately. Its target is the document being closed, then it bubbles to the app, so there are two propagation steps for that event.

             

            To highlight this, let's create a generic event handler (evHandler) then attach it to any possible listener for the 'beforeClose' event:

             

            #targetengine 'test01'
            
            function evHandler(ev)
            {
                alert([
                    'evHandler being called.',
                    'target: ' + ev.target.constructor.name,
                    'listener: ' + ev.currentTarget.constructor.name,
                    'phase:  ' + ev.eventPhase
                    ].join('\r'));
            }
            
            // assuming that a document is open
            // just before you execute the script
            // ---
            app.activeWindow.addEventListener('beforeClose', evHandler);
            app.activeDocument.addEventListener('beforeClose', evHandler);
            app.addEventListener('beforeClose', evHandler);
            
            

             

            On closing the document's window, you will observe that evHandler is called 5 times, that is:

            • 3 times for the window's beforeClose event (ev1) which traverses LayoutWindow, Document and Application, in that order;

            • 2 times for the document's beforeClose event (ev2) which traverses Document and Application, in that order.

             

            Now if you restart InDesign, set up a similar context and append the line

             

            ev.stopPropagation();
            

             

            to evHandler before you run the script, then you observe that evHandler is now called twice, that is, once for each event. Only the AT_TARGET phase is reported, because both the window and the document event propagation is stopped as soon as the event has been managed (at the target stage). That's the actual meaning of stopPropagation(). This method only prevents a specific event from being handled by higher listeners during the bubbling mechanism. More on this topic can be found here: http://www.quirksmode.org/js/events_order.html

             

            But as I said above, there is usually no point in handling events from volatile listeners such as Document or Window instances when dealing with InDesign DOM events. Most scripts set listeners and handlers at the Application level, since this is a stable entity that can listen to any event (whatever its target).

             

            Hope that helps.

             

            @+

            Marc

            • 3. Re: Confused by stopPropagation()
              RobertKyle Level 2

              Thank you so much for taking the time. Your test script illustrates the point in a second way: When I opened and closed a second document, evHandler is only called twice (even without stopPropagration()) because nobody is listening to document 2 or its LayoutWindow. You knew that, of course, but it was the 'I get it' moment for me.

               

              I started noodling with this after writing a Script UI project that updates its menus whenever the user activates a new LayoutWindow. It works fine, but I realized that the afterActivate listener fires on a lot of extraneous events, even javascript alert()s. So I started worrying about whether I was wasting too many electrons picking through all the afterActivate events to find the ones targeted at a LayoutWindow. I can now imagine a world where the event listener is attached only to the window at the moment it is opened, but I gather from your remarks that it might not be worth the trouble.