Add URL hash to reveal.js "Upcoming" speaker notes slide

163 Views Asked by At

I am using reveal.js for a slide presentation. Reveal.js provides a "Speaker View" window. In Reveal.js's "Tall" layout, it shows an "Upcoming" slide in an overlay box called "Upcoming".

To help me keep track of where I am, I would like to change that text to include the hash portion of the URL, like "#1" or #/1/0/1". (The hash portion of a Reveal.js URL is its slide-deck position.)

How can I do this?

Here is a screenshot of the "Speaker View" window:

reveal.js speaker window in tall format

2

There are 2 best solutions below

4
Damzaky On

I believe there is a way better solution than this, but if you need a quick, easy and safe solution, I think you can use this. Seeing the documentation, I don't think there's any API for this, so I had to come up with own solution. The way I see it, the speaker view is using iframe to show the upcoming slide and unfortunately it doesn't send any callback when the upcoming slide has changed (so the speaker view doesn't know the state of the upcoming slide). Normally, you would want to use postMessage to send back the state after the upcoming iframe succeed changing the slide, but this approach might break something if you're not careful. So my proposed approach is to basically synchronize the Upcoming label with the hash url of the iframe. The easiest way to do this is by using setInterval to do a polling and get the hash value and append it to the Upcoming label.

First thing you must do is to open this file plugin/notes/speaker-view.html (I assume you have this file in your project directory since it should be added when you clone the original repository), then look for this function setupIframes( data ), then add these codes at the end of the function, like so:

        /**
         * Creates the preview iframes.
         */
        function setupIframes( data ) {
          ...
          upcomingSlide.setAttribute("height", 512);
          upcomingSlide.setAttribute("src", upcomingURL);


          // ADD BELOW CODES
          setInterval(function () {
            const upcomingLabel = document.querySelector(
              "#upcoming-slide .overlay-element"
            );
            upcomingLabel.innerHTML =
              "Upcoming " + upcomingSlide.contentWindow.location.hash;
          }, 300);


          ...
          document.querySelector("#upcoming-slide").appendChild(upcomingSlide);
        }

After that, you need to rebuild the project, run this command:

npm run build

After the build is done, you can start run the project again. You can also change the 300 (300ms) to maybe 1000 (1s) if you want, the smaller the values, the more synchronized it will be.

If you're wondering why I don't propose using hashchange event on the iframe, it's because it doesn't work on this case, I tested it.

6
Heiko Theißen On

Adding to Damzaky's answer, the change of the fragment of the iframed URL can happen either via location.hash = ... or via history.replaceState(...) (see here), and the latter variant does not trigger the hashchange event.

I therefore suggest to introduce a new event slide and register a handler for this on the upcomingSlide window.

In function writeURL:

if( config.history ) {
  window.location.hash = hash;
  window.dispatchEvent(new Event("slide")); // inserted line
}

In function replaceState:

replaceState( url ) {
  window.history.replaceState( null, null, url );
  this.replaceStateTimestamp = Date.now();
  window.dispatchEvent(new Event("slide")); // inserted line
}

In speaker-view.html:

upcomingSlide = document.createElement( 'iframe' );
upcomingSlide.setAttribute( 'width', 640 );
upcomingSlide.setAttribute( 'height', 512 );
upcomingSlide.addEventListener("load", function() { // inserted block
  upcomingSlide.contentWindow.addEventListener("slide", function() {
    const upcomingLabel = document.querySelector(
      "#upcoming-slide .overlay-element"
    );
    upcomingLabel.innerHTML =
      "Upcoming " + upcomingSlide.contentWindow.location.hash;
  });
});
upcomingSlide.setAttribute( 'src', upcomingURL );

The load event occurs when a page (a new presentation) is loaded into the iframe upcomingSlide. At this point, the upcomingSlide.contentWindow is available and can register its event handler.

That removes the need to use setInterval.