Archive | March 2014

Dojo in XPages – 18: Event Delegation

We recently saw how to dynamically attach event handlers to DOM elements with dojo.on(). In this post, we’ll see how to use event delegation to set up event handlers that will work on DOM elements that don’t even exist on the page yet. This is powerful logic that is well worth learning.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

Event Binding vs Event Delegation

When you use the examples in the previous post to attach event handlers to DOM elements with dojo.on(), you’re binding that event handling function to the selected element(s) currently on the page. However, if the elements on the page change and new elements are later created, they will not have the event handler bound to them.

Event delegation solves this problem by looking for the desired elements from a higher level (for example, the page body or the window object). This way, if elements are added later that meet the selection criteria, they will still handle the event as desired.

Check out this post by Marky Roden for further explanation.

A Use Case for Event Delegation

A common XPages use case will illustrate the point. Take, for example, a repeat control (that creates a table row for each instance) or view panel that uses a pager.

If you run this code on the page, you will get an event handler that writes a message to the browser console each time a table row is clicked.

dojo.query('tr').on('click', function() {
  console.log ('Clicked Row');
});

However, if you use the pager to load the next set of rows, then nothing will happen when you click on a row. This is because the event handler was bound to all table rows that existed when the code was run. Once the pager is clicked, those rows were removed and a new set was created on the page.

If you want your function to run any time any row in the view or repeat is clicked, even after paging, you can use event delegation.

The code is very similar, but instead of just specifying an event to handle, you specify also specify a selector to choose the elements as well as the event to handle, in this form selector:event. The other difference is that the first parameter will not be the elements to select, but rather a higher-level element that will contain the elements that you want to handle.

It can take one of these forms:

dojo.on(window.document, [selector[:[event], function() {} );
dojo.query('body').on([selector]:[event], function() {} );

(You can also substitute the name of an existing function in place of the anonymous inline function declaration.)

For example, this will delegate the event by putting the listener on the page body. It will listen for a click event on all tr tags within the page body, regardless of whether they exist now or are created at a later time via client-side code or a partial refresh. (If the entire page is refreshed, then the handler would go away.)

dojo.query('body').on('tr:click', function() {
  console.log('Clicked Row')
});

The selector uses dojo.query(), so the selector will follow the same rules used for selecting elements with dojo.query().

Here’s another example that will do the same thing, but will save the first call to dojo.query(), so it would seem like it should be more efficient. This uses the dojo.on() method directly (rather than chained to a dojo.query()). Rather than querying an element, you can just set it to the top-level window.document, so it will listen for events on the selected elements anywhere on the page.

dojo.on(window.document, 'tr:click', function() {
  console.log('Clicked Row');
});

However, if you want to target the listening to a smaller area of the page, you may want to instead use dojo.query() to narrow the scope of listening for the events and elements.

Note that you need to include the dojo.on module on the page if you want to use the dojo.on() method in XPages, but you don’t need to include any modules if you use dojo.query().on().

Advertisements

Dojo in XPages – 17: Getting Event Information from Event Handlers

In the last post, we looked at how to dynamically attach event handlers to DOM elements with Dojo. In this post, we’ll take a look at the information available to the event object that is available to the event handler.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

Getting The Event Object

If you add a parameter to the event handler that you attach to any DOM element via Dojo, you will have a handle to an Event object containing informomation about the event.

Here’s an example:

dojo.on(dojo.byId('myDiv'), 'click', function(e) {
  // do something
});

Event Coordinates

The layerX and layerY properties provide the coordinates of the event (such as a click) relative to the event target and the pageX and pageY properties provide the coordinates of the event relative to the page.

If the element flows inline on the page, then the layer and page coordinates generally seem to be the same. If the element is absolutely positioned on the page, then the layer properties are relative to the “layer” that is absolutely positioned.

You can test these properties with code like this:

dojo.on(dojo.byId('myDiv'), 'click', function(e) {
  console.log('clicked div');
  console.log('layerX: ' + e.layerX);
  console.log('layerY: ' + e.layerY);
  console.log('pageX: ' + e.pageX);
  console.log('pageY: ' + e.pageY);
});

To see the difference in the layer properties, change the div from something like this:

<div id="myDiv"
  style="height:100px; width:100px; background-color:red;">
</div>

…to this (specifying the positioning):

<div id="myDiv"
  style="height:100px; width:100px; background-color:red; position:absolute; top:50px; left:50px;">
</div>

target vs currentTarget

The target property is the DOM element that originally received the event. The currentTarget property is the DOM element whose event handlers are currently being processed. They will be the same if you click on one element and the event does not trigger any other handlers.

Here’s an example of how they can be different. If you have a div on the page with the id of myDiv and you run the code below to attach a click event handler to both that div and the page body, you can click on the div and it will fire the click event on both the div and the page body (since they’re both listening for the click event).

In this case the event handler for the div will fire first and will show both the target and currentTarget to be myDiv. Then the body‘s event handler will run and it will show the target to be myDiv, because that was the element that first received the event.

dojo.query('body').on('click', function(e) {
  console.log('clicked page body');
  console.log('target: ' + e.target.id);
  console.log('currentTarget: ' + e.currentTarget.id);
});

dojo.on(dojo.byId('myDiv'), 'click', function(e) {
  console.log('clicked div');
  console.log('target: ' + e.target.id);
  console.log('currentTarget: ' + e.currentTarget.id);
});

Event Bubbling

As we saw in the previous example, the click event will be fired for both the div and the body that have handlers.

If desired, you can stop this bubbling behavior with the stopPropagation() method of the event.

dojo.on(dojo.byId('myDiv'), 'click', function(e) {
  console.log('clicked div');
  console.log('target: ' + e.target.id);
  console.log('currentTarget: ' + e.currentTarget.id);
  e.stopPropagation();
});

This prevents the event from being passed on to the body‘s event handler.

The preventDefault() method of the event object will prevent default behavior from firing after the event handler. For example, if you have a link, but you attach a handler to the click event and then call e.preventDefault(), the link will not be opened.

This code will log a console message and disable any link on the page:

dojo.query('a').on('click', function(e) {
  console.log('clicked link');
  e.preventDefault();
});

Reading Key Presses

Another useful piece of information to trap with event handlers is a keystroke. The charCode() method will return a character code for a printable character and the keyCode() method will return a character code for a non-printable character, which is very handy to trap special keys.

This code will display the charCode or keyCode for anything typed into an edit box named inputText1. It will also trap the Enter key and prevent it from automatically submitting the page.

dojo.query('[id$=inputText1]').on('keypress', function(e) {
  console.log('keypress');
  console.log('charCode: ' + e.charCode);
  console.log('keyCode: ' + e.keyCode);
  
  // Stop [Enter] from submitting form
  if (e.keyCode == 13) {
    console.log('trapping [Enter] key');
   e.preventDefault();
  }
});

If you want your keyCode-checking code to be a little more readable, you can include the dojo.keys module and compare the keyCode to a list of constants. (See this page for more information.)

Dojo in XPages – 16: Attaching Event Handlers with dojo.on()

In this post, I’ll show how to dynamically add event handlers to one or more events on one or more DOM elements with dojo.on().

Dojo in XPages Series

Dojo in XPages — All Blog Posts

Including the dojo.on module

The dojo.on module is not automatically available, so you need to add it either to the page/custom control as a resource or to the entire application via the theme. (See this post for more information).

This allows you to dynamically add event handlers as needed.

Using dojo.on()

To attach an event handler, you use the method in this form: dojo.on(DOM element, event, function)

For example, to add an event handler when the user clicks a button with the id of myButton, you could use this code:

dojo.on(dojo.byId('myButton'), 'click', function() {
  console.log('clicked button');
});

You could also define the function by name rather than inline.

function logClick() {
  console.log('clicked button');
}

dojo.on(dojo.byId('myButton'), 'click', logClick);

This allows you to refer to more complex functions or functions that exist in libraries or elsewhere on the page.

Adding Event Handlers to Multiple Elements

You can also add event handlers to multiple elements at the same time by chaining the on() event to a dojo.query().

This code will add a click event handler to all table rows on the page:

dojo.query('tr').on('click', function() {
  console.log('clicked table row');
});

Since you’re chaining this to a query that’s selecting the elements, you only have to specify the event and the function when calling the on() method.

Event Handler Timing

The timing of connecting events is very important. If you run this code immediately as the page loads, the DOM elements may not be available for attaching event handlers.

If you need the code to run right away, put it in the onClientLoad event of the page or custom control. This is the dojo event that fires once the page is fully loaded.

Otherwise, you can run it dynamically when triggered by some other page event.

Adding Handlers to Multiple Events

You can attach an event handler to multiple event types with a single call by comma-delimiting the event names.

This code will attach an event handler to the click, double-click, and mouseover events and also demonstrate how to determine the type of event that was triggered:

dojo.on(dojo.byId('myButton'), 'click, dblclick, mouseover', function() {
  console.log('triggered button event');
});

We’ll look more at dealing with event info in the next post.

Disconnecting an Event Handler

If you want to disconnect an event handler, you can keep a handle to it at the time you attach it and then use remove() method to drop it.

This code will set up an event handler and store it in a variable.

var handle = dojo.on(dojo.byId('myButton'), 'click', function() {
  console.log('clicked button');
});

This code will remove the event handler:

handle.remove();

Firing an Event Handler Once, then Automatically Disconnecting

If you want to attach an event handler that will only fire one time and then remove itself, you can use the dojo.on.once() method.

This code will add an event handler to write a message to the console on the first click and then stop listening for the event.

dojo.on.once(dojo.byId('myButton'), 'click', function() {
  console.log('clicked button');
});

Backwards Compatibility Note

The dojo.on() method is new as of Dojo 1.7, so it’ll work in Notes 9.

The previous method is dojo.connect(), which still works for now, but is deprecated and may be removed when dojo 2 is released. (Although this is not really a concern for an XPages application until Notes starts including Dojo 2.)

Dojo in XPages – 15: Modifying the Rate of Animation with Easing Functions

In the last few posts, we’ve looked at dojo code to perform pre-defined animations and custom animations. By default, the animations progress in a linear fashion, but the rate of animation can be changed. In this post, we’ll take a look at the easing functions that are available to modify animations.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

Built-In Easing Functions

The easing property of an animation defines the rate of the animation.

You can write your own function to use, but there are 31 built-in functions already available to save you some time.

The default is linear — it performs the transition evenly throughout. The other methods are mathematical calculations that change the rate of the transition at the beginning or end (or both).

Easing Function Effect on Transition
linear No effect — even throughout
quadIn, quadOut, quadInOut rate by power of 2
cubicIn, cubicOut, cubicInOut rate by power of 3
quartIn, quartOut, quartInOut rate by power of 4
quintIn, quintOut, quintInOut rate by power of 5
sineIn, sineOut, sineInOut rate by sine function over time
expoIn, expoOut, expoInOut exponential effect
circIn, circOut, circInOut circular effect
backIn, backOut, backInOut start away from target, then move toward it
elasticIn, elasticOut, elasticInOut back and forth, then toward target
bounceIn, bounceOut, bounceInOut bounce near the beginning or end

Naming Convention

The functions are all named consistently. They specify the affect and at which end(s) the effect will be applied.

  • The *In functions start slowly and then speed up toward the end
  • The *Out functions start faster and end slowly (the opposite of the *In functions)
  • The *InOut functions start slowly, speed up, then end slowly

Dojo Modules to Include

The easing functions are part of the dojo.fx.easing module, so you’ll need to include it on your page in order to use them. (This is in addition to including dojo.fx if you’re using a function in that module.)

Example

Here’s an example that will take a div called myDiv on the page and move it, using the bounceIn easing function:

dojo.fx.slideTo({
  node:"myDiv",
  top: 100,
  left: 400,
  easing: dojo.fx.easing.bounceIn,
  duration: 3000
}).play();

More Examples

There’s a good demo already set up in the Dojo documentation that allows you to test each one of these easing functions.

https://dojotoolkit.org/reference-guide/1.8/dojo/fx/easing.html#examples

Just click the Play button and you can choose a function to try. Test them out and use whatever suits your needs best

Dojo in XPages – 14: Custom Animations

In the last post, we looked at the dojo code to perform pre-defined animations. In reality, those are wrappers for the actual animation function. In this post, I’ll show how to animate any DOM element property so you can create your own custom animations.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

dojo.animateProperty()

The Fade In/Out methods animate opacity attribute, the Wipe In/Out methods animate the height attribute, and the Slide To method animates the top and left attributes of an element.

The built-in methods are handy, bu you can write these same animations yourself pretty simply as well. This is good news because you can also easily modify the behavior as needed.

The animateProperty() method allows you to do this. It is part of core dojo — you don’t need to load the dojo.fx module in order to use it.

Custom Animation Simple Action Source

In a previous post I showed the simple actions that are available to create Dojo animations in XPages.

The custom animation shown in that post changed the background color (from red to blue) and the height (from 100px to 200px) of a simple div on the page.

Here’s the code that the simple action generated:

function view__id1__id9_clientSide_onclick(thisEvent) {
  var _id=dojo.byId("myDiv");
  var _a1 = dojo.animateProperty({"node":_id,"properties": {"backgroundColor": {"start":"#FF0000","end":"#0000FF"},"height": {"start":"100","unit":"px","end":"200"}}});
  _a1.play();
}

Here’s how you could write that yourself to perform that same animation

dojo.animateProperty({
  "node":"myDiv",
  "properties": {
    "backgroundColor": {"start":"#FF0000","end":"#0000FF"},
    "height": {"start":"100","unit":"px","end":"200"}
  }
}).play();

Another Example — Changing the Wipe Direction

The built in methods (in dojo.fx) to wipeIn() and wipeOut() an element work by modifying the height of the element. If you’d like to create a horizontal wipe effect, you can animate the width property instead.

This will wipe it out horizontally:

dojo.animateProperty({
  "node":"myDiv",
  "properties": {
    "width":{"end":"0"}
  }
}).play();

This will wipe it back in horizontally:

dojo.animateProperty({
  "node":"myDiv",
  "properties": {
    "width":{"end":"200"}
  }
}).play();

Additional Animation Properties

In addition to the required animation attributes, you can also define a delay before starting the animation, the duration of the animation (default is 350 milliseconds) and an easing function to modify the effect. (We’ll come back to that in the next post.) You can also change the frame rate and a repeat property to play the animation more than once.

Animation Events

I’m not going to dig into it here, but there are 5 animation events to which you can attach handlers if you need to trigger another action:

  • beforeBegin
  • onBegin
  • onEnd
  • onPlay
  • onAnimate

Combining Animation of Mulitple Elements

If you want to animate multiple elements concurrently, you can define the animations as objects and use the combine() method to run them together. Since this method is part of the dojo.fx module, you’ll need to include that module on the page.

For example, if I have two divs on my page (myDiv and myOtherDiv) and I want to make the first one smaller and the second one larger, I can define both of those animations and chain them together like this:

var div1Animation = dojo.animateProperty({
  "node":"myDiv",
  "properties": {
    "width":{"end":"50"}
  }
});

var div2Animation = dojo.animateProperty({
  "node":"myOtherDiv",
  "properties": {
    "width":{"end":"300"}
  }
});

dojo.fx.combine([div1Animation, div2Animation]).play();

The combine() method takes an array of animation objects.

Up Next

At this point, you’re armed to create your own custom animations! In the next post we’ll look at the built-in easing methods available to modify the timing of how the animations play out.

Dojo in XPages 13 – DOM Element Animation Functions

In the last post, we saw how to animate DOM elements with simple actions built into XPages. In this post, we’ll look at the underlying Dojo code they generate and see how to create them without the simple actions, which allows you to add animations to be triggered by non-XPages components and lays the foundation for creating fully-customized animations.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

Using Built-In Animation Methods

Dojo has a some common methods built in for easy use.

The general concept is that you call an animation method and define its parameters, then call the play() method to run it.

Fade Out

This is the code that is generated by the simple action:

function view__id1__id4_clientSide_onclick(thisEvent) {
  var _id=dojo.byId("myDiv");
  var _a1 = dojo.fadeOut({"node":_id});
  _a1.play();
}

Here’s how you can write it yourself:

dojo.fadeOut({'node':'myDiv'}).play();

This is not the recommended syntax, but it seems to work as well:

dojo.fadeOut('myDiv').play();

The initial syntax is required when you want to add more attributes to the animation. To change the duration of the animation, specify the value in milliseconds. Here are two ways you can write this:

fadeParameters = {'node':'myDiv', 'duration':1000};
dojo.fadeOut(fadeParameters).play();
dojo.fadeOut({'node':'myDiv', 'duration':1000}).play();

Fade In

Here’s the code generated by the simple action:

function view__id1__id5_clientSide_onclick(thisEvent) {
  var _id=dojo.byId("myDiv");
  var _a1 = dojo.fadeIn({"node":_id});
  _a1.play();
}

Here’s how you can write it yourself:

dojo.fadeIn({'node':'myDiv'}).play();

Interestingly, this does not seem to work, even though it works for fadeOut()

dojo.fadeIn('myDiv').play();

To change the duration of the animation, add the duration attribute.

dojo.fadeIn({'node':'myDiv', 'duration':1000}).play();

Transitions in dojo.fx

The wipe transitions do not have corresponding methods in core dojo, but they do exist in the dojo.fx module.

When you use any of the simple actions, it automatically includes the module for you. It adds this script tag in the page header:

<script type="text/javascript">dojo.require('dojo.fx')</script>

If you’re not using a simple action transition on the page, then you’ll need to include the module yoursef. If you don’t, you’ll see a ‘dojo.fx is undefined’ error in the developer tools console:

Dojo13_a_fxError

You can include the module on your page or at the application level via the theme. At the page level, go to Resources > Add > Dojo module... and enter the module name.

Dojo13_b_Include_fx

This puts the same script tag shown above into the page header.

Wipe Out

Here’s the code that’s generated for the Wipe Out simple action:

function view__id1__id6_clientSide_onclick(thisEvent) {
  var _id=dojo.byId("myDiv");
  var _a1 = dojo.fx.wipeOut({"node":_id});
  _a1.play();
}

Here’s how you can write it yourself:

dojo.fx.wipeOut({'node':'myDiv'}).play();

Wipe In

The Wipe In method is very similar, but it also needs a parameter for the final height of the object.

Here’s the code generated by the simple action:

function view__id1__id7_clientSide_onclick(thisEvent) {
  var _id=dojo.byId("myDiv");
  var _a1 = dojo.fx.wipeIn({"node":_id,"height":100});
  _a1.play();
}

Here’s how you can write it yourself:

dojo.fx.wipeIn({'node':'myDiv','height':100}).play()

Slide To

The Slide To function moves an element by animating the top and left position to the desired place on the page.

Here’s the code generated by the simple action:

function view__id1__id10_clientSide_onclick(thisEvent) {
  var _id=dojo.byId("myDiv");
  var _a1 = dojo.fx.slideTo({"node":_id,"top":400,"left":400});
  _a1.play();
} 

Here’s how you can write it yourself:

dojo.fx.slideTo({'node':'myDiv','top':400,'left':400}).play();

Up Next

These built-in methods make it easy to animate some elements on the page but don’t provide much opportunity for customization. In the next post, we’ll take a look at the method you can use to define custom animations on your page.

Dojo in XPages 12 – Animating DOM Elements with Simple Actions

One of the great features of Dojo is that it provides the ability to animate DOM elements. The easiest way to make use of that functionality in an XPages application is the built-in simple actions. In this post, we’ll look at the actions that are available and how to use them.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

Animation Simple Actions

The easiest way to use common Dojo animations is to use the simple actions that are part of the Extension Library (and included with Notes 8.5.3 Upgrade Pack 1 and built into Notes 9).

To find them, select a component to which you can add an event handler. On the Events view, click the Client tab, ensure that Simple Actions is selected and click Add Action...

Change the Category drop-down to Dojo Effects and you’ll see a list of options.

Dojo_12a

  • Dojo Animation – Animates one or more attributes
  • Dojo Fade In Effect – Fades in a hidden element
  • Dojo Fade Out Effect – Fades out an element
  • Dojo Slide To Effect – Slides an element to a specific position
  • Dojo Wipe In Effect – Fades in a hidden element with a wipe in effect
  • Dojo Wipe Out Effect – Fades out an element with a wipe effect

Fading

All you have to do to use the fade and wipe actions is define the target node. You can choose an XPages component or define a hard-coded or computed client-side element ID.

For a page with a div that has an id of “mydiv”, these simple actions will fade the div out and back in:

Dojo_12b_FadeOut

Dojo_12c_FadeIn

The fade effects steadily change the opacity of the element from 1 to 0 and vice versa.

The duration specifies how long the animation will take to complete (in milliseconds). The default is 350 milliseconds. The easing property can take a javascript function that defines the rate of the animation. You can cause it to speed up or slow down or modulate.

Wiping

Similarly to the Fade animations, you can define the target node and the Wipe actions will work.

However, because this animates the height, be careful of using this with div tags. Once you wipe a div out, its height attribute is changed to auto, rather than its originally-defined. When you try to execute the Wipe In effect, there’s no height defined for the div, so it doesn’t display anything (aside from a small line during the animation, which goes away)! This appears to be the case both when the div is styled inline or with a class, using CSS. (The same issue does not seem to apply for other elements.)

SlideTo

The SlideTo animation moves an element on the page to a specific position, as defined by the top and left properties.

Dojo_12d_SlideTo

You can change the duration to speed it up or slow it down. You can also define an easing function to modify whether it speeds up or slows down.

Custom Animation

Aside from the built-in effects mentioned above, you can use the Dojo Animation action to animate other properties.

In addition to specifying the target element, you also define one or more properties to animate and the end state of each property. (It will start with the element’s current attributes if you don’t define start parameters.)

Here’s an example that will change the div color from red to blue and change its height from 100px to 200px:

Dojo_12e_Custom

NOTE: As mentioned in other recent posts, Dojo attribute names are very similar to CSS attribute names, but they do not allow hyphens. The background-color css attribute is defined as backgroundColor for this animation.

You can set the repeat property to perform the animation mutiple times, although it appears that you have to define the starting values if you do that. And the element ends up in its original state, rather than in the defined end state.

You can also set the frame rate and animation duration. You can also set a delay before the animation begins.

Up Next

In the next post, we’ll look at the dojo.fx module and see how to generate these animations with client-side code. This will allow you to further customize the animations and also to attach them to non-XPages elements.