HTML Interfaces + Ink: Part 1: Speaker CSS

HTML Interfaces + Ink

Note: This series is a sequel to JavaScript + Ink series. It builds on the code and concepts found there.


Speaker CSS

When using the Continue() function to get the next text block from the Ink runtime, the result is a string. While this seems very obvious, it also means that the string result can be parsed before showing it to a user.

As a bridge between the Ink runtime and a user, JavaScript running in a browser can act on the contents of the string and even use CSS to style text before the user sees it.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example Dialogue</title>
<link rel="stylesheet" type="text/css" href="site.css">
</head>
<body>
<div id="story"></div>
<script src="ink.js"></script>
<script src="StoryDialogue.js"></script>
<script src="main.js"></script>
</body>
</html>
view raw index.html hosted with ❤ by GitHub

Following the pattern setup in the last series, the Ink.js and story.js (called StoryDialogue.js in this example) are included along with a main.js file. This also follows the same setup as the Ink for Web functionality. (See JavaScript + Ink: Part 1: Ink for Web for more details.)

// Use the storyContent to create an Ink story
var story = new inkjs.Story(storyContent);
// Save a reference to the storyContainer where text will be added
var storyContainer = document.getElementById("story");
function continueStory() {
// Get ink to generate the next paragraph
var paragraphText = story.Continue();
// Find the position where the colon start
var dialogueTag = paragraphText.indexOf(':');
// Get the string from the beginning to the position of colon
var speaker = paragraphText.substring(0, dialogueTag);
// Create paragraph element
var paragraphElement = document.createElement('p');
// Add a CSS class matching the speaker
paragraphElement.classList.add(speaker);
// Add the text to the P-element
paragraphElement.innerHTML = paragraphText;
// Append the P-element to the storyContainer
storyContainer.appendChild(paragraphElement);
}
// Call continueStory() to start
continueStory();
view raw main.js hosted with ❤ by GitHub

The main.js file looks similar to other examples from the previous series. It loads the storyContents variable and saves a reference to an element with the id of “story”.

The continueStory() function is where some changes happen.


  // Get ink to generate the next paragraph
  var paragraphText = story.Continue();

  // Find the position where the colon start
  var dialogueTag = paragraphText.indexOf(':');
  // Get the string from the beginning to the position of colon
  var speaker = paragraphText.substring(0, dialogueTag);

The results of the call to story.Continue() is saved. Then, the indexOf() function is used to get the position of the colon within the string. Next, it is used to get the speaker by getting a substring between the first position and the position of the colon within the string. In other words, it gets name of the speaker.

For this to make sense, consider the following Ink code:

Dan: I really don't know what I'm going to do with them, Susan. They are driving me crazy.
Susan: I hear you, but what, really, can you do?
Dan: I don't know. I just… I wish they would listen to me.
Susan: Dan. They are kittens. You are literally herding cats. They don't listen to anyone.
Dan: Okay. Sure. That's a valid point. But, I mean, just look! These kittens are crawling up everything in the room.

The text of the story is broken up with a format where the speaker of the lines appear before the text they speak. It is separated by a colon.

Coming back to the main.js code, this can then be used to add a CSS class to the paragraph to mark who is speaking the lines.

.Dan {
color: green;
}
.Susan {
color: purple;
}
view raw site.css hosted with ❤ by GitHub

The site.css file simply includes the names of the speakers as CSS classes with rules for how their text should be shown to the user. As the text is parsed as received by Continue(), these classes are applied to the paragraphs as they occur in the story.

Revising main.js

Showing only a single block of text from the Ink runtime is not very helpful. To fix that issue, an event listener could be added to add to the code to run continueStory() each time.

Within the continueStory() code, some additional checks could be added through using canContinue to check if there is more content to show and checking if there is a speaker or not. (The text “End of Story,” for example, would not have a speaker and thus needs to be checked against somehow.)

// Use the storyContent to create an Ink story
var story = new inkjs.Story(storyContent);
// Save a reference to the storyContainer where text will be added
var storyContainer = document.getElementById("story");
function continueStory() {
// Check if there is more content in the story
if(story.canContinue) {
// Get ink to generate the next paragraph
var paragraphText = story.Continue();
// Find the position where the colon start
var dialogueTag = paragraphText.indexOf(':');
// Get the string from the beginning to the position of colon
var speaker = paragraphText.substring(0, dialogueTag);
// Create paragraph element
var paragraphElement = document.createElement('p');
// Check if speaker is an empty string
if(speaker.length > 0) {
// Add a CSS class matching the speaker
paragraphElement.classList.add(speaker);
}
// Add the text to the P-element
paragraphElement.innerHTML = paragraphText;
// Append the P-element to the storyContainer
storyContainer.appendChild(paragraphElement);
}
}
// Call continueStory() to start
continueStory();
// Listen for the "click" event and show text
window.addEventListener("click", continueStory);
view raw main.js hosted with ❤ by GitHub