Building a Character Sheet: Colouring Using JQuery in Roll20

I haven’t made a post on using Roll2o version of JQuery yet (aka roll20Query), and I’ve used it in the TurboPulp sheet so I’ll describe it here.

JQuery in standard webdesign is a wway to use javascript in a variety of ways, to rewrite the entire page, aka the document model. On Roll20, we aren’t able to modify the document model (for very good reasons – if we could, we could reshape the GM’s entire adventure), so our use of JQuery is quite a bit more limited. We use it basically to add or remove CSS Classes to elements on a character sheet.

This does enable some nifty effects. For example, on the TurboPulp sheet we have a section for a character’s skills, like this:

With the application of some code that becomes

(colours subject to change.)

What Happened Here?

The original pic is a bit bland, but each button could be made more attractive. In the second pic, each button has been colour-coded according to the character’s skill. So, at a glance you can see the skill of a character.

The code to do this is short, but complex, so let’s describe it. Here’s the worker:

on(`sheet:opened change:colouring ${skills.reduce((all, one) => `${all} change:${one}`,'')}`, () => {
        getAttrs([...skills, 'colouring'], v => {

            skills.forEach(skill => {
                ranks.forEach(rank => {
                    $20(`.container .${skill}`).removeClass(rank);
                });
            });
            const colouring = int(v.colouring);
            if(!colouring) return;

            skills.forEach(skill => {
                ranks.forEach(rank => {
                    const rank_upper = capitalize(rank);
                    if (v[skill] == rank_upper) {
                        $20(`.container .${skill}`).addClass(rank);
                    }
                });
            });
        });
    });Code language: JavaScript (javascript)

Prerequisites

I needed several things for this to work. First,, each attribute’s button was give a class that matched its name, like this

    <button type="roll" name="roll_Audacity" class="nod20 audacity" value="@{audacity_button_code}">
        <span>Audacity</span>
    </button>Code language: HTML, XML (xml)

Notice the button here is given the class name audacity — that will make it easy to identify it by the skill later.

Then, a CSS class was created for each rank, like this:

.fair {
    font-weight: bold;
    background: yellow;
    color: black;
}Code language: CSS (css)

Finally, in the script block, we have an array for the rank names and the skill names. These are useful in several places, so it’s a bonus they already exist.

    const skills = [ "audacity",  "daring",  "ferocity",  "flair",  "shenanigans",  "tinkering", ];
    const ranks = [ "terrible",  "poor",  "fair",  "good",  "great",  "superb",  "spectacular",  "legendary",  "mythical",  "perfect", ];Code language: JavaScript (javascript)

For CSS and Javascript, case matters, so always use lower-case: it’ll make your life easier. (I say use lower case and not upper case, which would stand out more, for a reason. There is at least one thing in JS that requires lower-case, and does not work without it, so if you have to pick upper or lower case, it should be lower case).

I use title case (starting with capital letters) in some places, so it was handy I created a user function to convert lower case to title case:

const capitalize = words => words.split(" ").map(s => String(s[0]).toUpperCase() + String(s).slice(1).toLowerCase()).join(" ");Code language: JavaScript (javascript)

JavaScript has a lot of ways to manipulate strings (words, sentences, groups of letters and numbers), so there are lots of ways to do this kind of thing.

Going Through The Code

Now that we have the basics set up, lets look at how that $20 function works. ($20 is the term roll20 uses for jquery).

on(`sheet:opened change:colouring ${skills.reduce((all, one) => `${all} change:${one}`,'')}`, () => {
        getAttrs([...skills, 'colouring'], v => {Code language: JavaScript (javascript)

The first line, the event line, tells us what triggers a sheet worker – when it actually runs. We want it to run when the sheet opens, to reset any colouring. Roll20’s JQuery does not remember its state, so you often want it to run on sheet:opened.

The attribute colouring has a value of 1 or 0, and just basically says, yes or no. Either its on or off. We want to run this worker when this value change, so that colouring can be applied when needed, and removed when not.

Then we have a reduce function, which basically takes the array of skill names, and converts them all to change:skill_name – since that array exists already, we can use it here in a fancy way to avoid some typing.

            skills.forEach(skill => {
                ranks.forEach(rank => {
                    $20(`.container .${skill}`).removeClass(rank);
                });
            });Code language: JavaScript (javascript)

Here we have a simple loop that looks at every skill, then removes every rank class from that.

Note that $20 wiorks by matching CSS declarations. Here we are looking for every string that matces .container .audacity then .container .daring and so on, and removing the classes .terrible, .poor, .fair, and so on from that code. So if there’s anything like

<button class="nod20 audacity good"Code language: HTML, XML (xml)

It’ll be found by this and become:

<button class="nod20 audacity"Code language: HTML, XML (xml)

This works because the entire sheet is placed inside a <div class="container">. I always create a container div to make things on the sheet easier to match.

            const colouring = int(v.colouring);
            if(!colouring) return;Code language: JavaScript (javascript)

Then we have a section of the code that looks if colouring should be applied,. If 1 (or yes) the rest of the worker is run – otherwise it stops here.

            skills.forEach(skill => {
                ranks.forEach(rank => {
                    const rank_upper = capitalize(rank);
                    if (v[skill] == rank_upper) {
                        $20(`.container .${skill}`).addClass(rank);
                    }
                });
            });
        });
    });Code language: JavaScript (javascript)

Finally, we apply colouring. We check every skill, and if it equals a particular rank, we add the rank class. And that’s it! Kind of complicated, and takes advantage of some pre-existing code, but it gets the job done.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.