This content originally appeared on Raymond Camden and was authored by Raymond Camden
I've been a fan of Reveal.js for many years. Reveal.js is a web-based presentation framework that makes it (mostly) easy to create slides with just basic HTML. I don't mind Powerpoint at all, and it's incredibly powerful, but when I'm presenting on web topics (which is, usually, 99% of the time), I don't like the experience of "alt-tabbing" from a running Powerpoint to code or browser tabs. Reveal.js helps me avoid that.
Using Reveal (I'm getting tired of typing the dot jay ess) is relatively simple. You add a script tag. You add two link tags for CSS (one for the core CSS and one for a theme) and then start writing HTML. The section
tag is used for each slide. Here's an example of how easy it is, from their docs:
<html> <head> <link rel="stylesheet" href="dist/reveal.css"> <link rel="stylesheet" href="dist/theme/white.css"> </head> <body> <div class="reveal"> <div class="slides"> <section>Slide 1</section> <section>Slide 2</section> </div> </div> <script src="dist/reveal.js"></script> <script> Reveal.initialize(); </script> </body></html>
In the snippet above, you can see the two section
tags representing each slide. There's quite a bit to it and if you want to see more, check out their demo, but for today, I was curious if I could simplify the creation of Reveal presentations with web components. Here's what I came up with.
Version One #
First, I'll share the HTML I wanted to use. I actually wrote this first and then began building the web component so I could see it update itself.
<reveal-preso theme="beige"> <section>Slide 1b</section> <section>Slide 2</section> <section>Slide 3</section> <section data-background-color="aquamarine"> <h2>🍦</h2> </section> <section data-background-image="https://placekitten.com/800/800"> <h2>Image</h2> </section> </reveal-preso>
My HTML makes use of the web component, reveal-preso
, and supports a theme
attribute. Inside the component are a set of section
tags for the slides. (As an FYI, I could have also built a reveal-slide
child tag, and I will do that in my next post!) I don't have any script or link tags at all. Now let's look at the component definition.
class RevealPreso extends HTMLElement { constructor() { super(); } connectedCallback() { this.theme = 'black'; if(this.hasAttribute('theme')) { this.theme = this.getAttribute('theme'); } let currentHTML = this.innerHTML; this.innerHTML = `<div class="reveal"> <div class="slides"> ${currentHTML} </div></div> `; // load reveal.js const script = document.createElement('script'); script.type = 'text/javascript'; script.async = true; script.src = 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.5.0/reveal.js'; document.head.appendChild(script); const link = document.createElement('link'); link.rel = 'stylesheet'; link.type='text/css'; link.href = 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.5.0/reveal.min.css'; document.head.appendChild(link); const theme = document.createElement('link'); theme.rel = 'stylesheet'; theme.type='text/css'; theme.href = `https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.5.0/theme/${this.theme}.min.css`; document.head.appendChild(theme); script.addEventListener('load', () => { Reveal.initialize(); }); }}if(!customElements.get('reveal-preso')) customElements.define('reveal-preso', RevealPreso);
As you can see, this is relatively simple. My connectedCallback
rewrites the inner HTML such that it's wrapped with the divs
that Reveal expects. I then dynamically load all three resources - my JavaScript and both CSS resources. I've got an event listener on the script such that when it's been loaded, I can initialize the presentation. From what I could see (via Googling and Stack Overflow), there's no onload
for CSS scripts. I saw a few suggested workarounds, but I decided to keep it simple and just worry about the JavaScript resource. I accept that may be problematic. One more issue - my code checks for the theme argument one time only. If you were to change it via JavaScript later, it would not pick up on that. (If I may be so bold as to suggest a best practice here, I'd probably suggest you always have code to recognize changes to attributes or clearly document what's not going to work.) Here's how it looks:
See the Pen WC Reveal by Raymond Camden (@cfjedimaster) on CodePen.
Personally, I think this is pretty cool! But notice that it expects the entire web view. That's the default for Reveal and probably how most people would use it, but I really wanted to see if it was possible to embed Reveal along with other web content. Here's how I did that.
Version Two #
First, I checked the docs. I had only ever used Reveal as a full web view presentation and I didn't know if the framework itself supported. Turns out, it absolutely has an embedded mode. There's also support for multiple embedded presentations. This requires two things - a slight change to how you initialize the Reveal object and supplying specific dimensions of the container holding your presentation.
I began by modifying my HTML a bit:
<h2>Preso</h2><p> Here is my preso. It brings all the boys to the yard.</p><reveal-preso height="700px" theme="dracula"> <section>Slide 1b</section> <section> <h2>Plan</h2> <ul> <li class="fragment">Phase One - Collect Underpants</li> <li class="fragment">Phase Two - ?</li> <li class="fragment">Phase Three - Profit</li> </ul> </section> <section>Slide 3</section> <section data-background-color="aquamarine"> <h2>🍦</h2> </section> <section data-background-image="https://placekitten.com/800/800"> <h2>Image</h2> </section> </reveal-preso><footer>End of page.</footer>
I've got some basic crap on top and bottom, and in the reveal-preso
tag, I've added a height
. My component is going to support both height
and width
, but it will default both. Now let's look at the updated component:
class RevealPreso extends HTMLElement { constructor() { super(); } connectedCallback() { console.log('connected callback called'); // defaults this.width = '100%'; this.height = '500px'; this.theme = 'black'; if(this.hasAttribute('width')) { this.width = this.getAttribute('width'); } if(this.hasAttribute('height')) { this.height = this.getAttribute('height'); } if(this.hasAttribute('theme')) { this.theme = this.getAttribute('theme'); } let currentHTML = this.innerHTML; this.innerHTML = `<div class="reveal" style="width: ${this.width}; height: ${this.height}"> <div class="slides"> ${currentHTML} </div></div> `; // load reveal.js const script = document.createElement('script'); script.type = 'text/javascript'; script.async = true; script.src = 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.5.0/reveal.js'; document.head.appendChild(script); const link = document.createElement('link'); link.rel = 'stylesheet'; link.type='text/css'; link.href = 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.5.0/reveal.min.css'; document.head.appendChild(link); const theme = document.createElement('link'); theme.rel = 'stylesheet'; theme.type='text/css'; theme.href = `https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.5.0/theme/${this.theme}.min.css`; document.head.appendChild(theme); script.addEventListener('load', () => { Reveal(this.querySelector( '.reveal' ), { embedded: true, keyboardCondition: 'focused' // only react to keys when focused }).initialize(); }); }}if(!customElements.get('reveal-preso')) customElements.define('reveal-preso', RevealPreso);
I've updated the code to look for height
and width
now and default both. When I rewrite the inner HTML content, I include those values. Lastly, I changed how I initialize the presentation. You can see this in action here:
See the Pen WC Reveal (2) by Raymond Camden (@cfjedimaster) on CodePen.
There are still things I'd tweak here. I'd definitely add support for changing the height and width. Reveal itself also supports recognizing changes to its size. One note - I did do a quick test with 2 presentations on the page, and it worked, but it only shows one theme which I believe there is no workaround as Reveal expects only one theme CSS. It may be possible, but I'm not sure.
Next Version? #
As I said earlier, I want to iterate on this one more time. I want to create a child tag, reveal-slide
(or perhaps something shorter) and look into importing the tag from another JavaScript resource. I also want to support dynamically changing the height and width. I'll work on this tomorrow or later this month. As always, let me know what you think!
This content originally appeared on Raymond Camden and was authored by Raymond Camden
Raymond Camden | Sciencx (2023-04-22T18:00:00+00:00) Creating a Web Component for Reveal.js. Retrieved from https://www.scien.cx/2023/04/22/creating-a-web-component-for-reveal-js/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.