Custom Actions

Extend Choicelab by creating your own actions using JavaScript.

If you're familiar with JavaScript, you can build your own actions in Choicelab, which can be used in your story just like you can with, say, audio or images.

Custom actions are fairly unrestricted in what they can do: they can manipulate elements anywhere on the page, and interact with other libraries and scripts. The biggest limitation, as with all Choicelab actions, is that if an action is inserting content onto the Choicelab stage, it will clear when the scene ends.

Basic example

A Choicelab action is created by calling the Action method, which takes two arguments:

  1. The name of the action you're registering: a single word, as you'd like to use it in your path files.
  2. An object whose properties define how the action works.

Let's say we want an action that changes the color of the current page when fired. We'll call it colorChange.

First, we'll set up the Action method. In our second argument, the render property defines what actually happens when the action fires in a scene:

Choicelab.Action('pageColorChange', {
  render: function(action) {
    // to be continued
  }
})

Before we fill out this function, let's consider what the colorChange action should do. Instead of literally providing various color values to change, it might be best to provide a general color attribute, which we can then key off of in CSS. We'll call this data-page-color, and put it on the <body> tag:

<body data-page-color="blue">
<!-- content -->
</body>
body[data-page-color="blue"] {
  background-color: rgb(97, 218, 251);
}

So, our action should be able to change the body tag's data-page-color attribute. In Choicelab, we might want to write that like so:

~~

Luro shrugged. "Well," they said. "Might as well keep going."

[4s]
[colorChange to="darkRed"]

Now let's go back to JavaScript. When an action fires in a scene, that individual instance of the action gets passed to the render function, with all of the properties included in the shortcode.

In our action above, we made a property named to, which specifies the color we want to switch to. In the render function, we can access that as action.to, so let's revisit our JS from earlier, and write this property to the console:

Choicelab.Action('pageColorChange', {
  render: function(action) {
    console.log(action.to); // "darkRed"
  }
})

This will give us the value darkRed.

Now instead, let's add the line we need to actually change this attribute:

Choicelab.Action('pageColorChange', {
  render: function(action) {
    document.body.setAttribute('data-page-color', action.to);
  }
})

We're almost done, but there's one important piece we need to add. Because Choicelab is a time-based experience, we need to explicitly tell Choicelab that the action has finished so that the scene can eventually end. To tell Choicelab the action is done, call the done() function:

Choicelab.Action('pageColorChange', {
  render: function(action) {
    document.body.setAttribute('data-page-color', action.to);
    action.done();
  }
})

And when we look at the <body> element in the DOM, we will now see:

<body data-page-color="darkRed">
<!-- content -->
</body>

That's it! Our pageColorChange action can now be used freely in any scene.

Considerations

  • Once a scene ends, Choicelab clears the stage of any actions rendered in the scene. If you want to use an action to control an element that will persist across multiple scenes, you should create and manage this element outside of the Choicelab stage.

Preloading assets

If your action loads and manipulates files, you'll want to load them before the scene starts, so that it doesn't affect the timing of the rest of the scene. You can use the loader property to define a function that Choicelab can run during its preloading steps.

What you include here largely depends on the file type being loaded. You might use Fetch or XMLHttpRequest; if your action incorporates another JavaScript library, it might have load functions of its own. The only thing that's required: at the end of the function, run action.loaded() so Choicelab knows it's ready.

You can assign the loaded file as a property of the action, and the action's render property can access it when the action fires:

Choicelab.Action('myAction', {
  loader: function(action) {
    // your loader code here, like...
    // action.myAsset = someVariable;

    // At the very end of the function...
    action.loaded();
  }
  render: function(action) {
    console.log( action.myAsset ); // someVariable
  }
})

Time-based and static actions

Some actions in Choicelab elapse over a period of time, like audio or video. Once a video in an action has finished playing, we can safely assume the action is also done. These are called time-based actions. Other actions, like text or images, don't elapse over a period of time; these are called static actions, because we don't know how long they should be on screen.

Static actions are almost always accompanied by timing flags, user input, or at least one time-based action to determine their length. If a scene has nothing but static actions and does not include any timing flags, Choicelab automatically inserts a continue button.

By default, custom actions are static, but you can make your action time-based by including a property in its action definition:

Choicelab.Action('myAction', {
  timeBased: true,
  // loader, render, etc.
})

This will prevent Choicelab from throwing a continue button onscreen in scenes that otherwise have only time-based actions.