Stephen Haney

Get contenteditable plaintext with correct linebreaks

What do you do if you need plaintext from a contenteditable HTML element with correct linebreaks?

It should be simple – just use element.innerText.

But TIL there are bugs with contenteditable and innerText linebreaks. For me, Chrome reports too many linebreaks. And I’m finding reports of FireFox doing the same.

// What my content editable looks like, a single line between
a

b

// What Chrome innerText reports, two lines between
a


b

I created a reproduction case codepen here, type type “a -> return -> return -> b” in the left and you’ll see the wrong result pop up on the right hand side (unless it’s been fixed by now!)

Where did this come from?

The problem, it seems, originates from browsers making up their own HTML for contenteditable. Some use divs, some use p tags, others prefer br. Chrome seems to mix and match divs and br elements even now.

No wonder innerText returns the wrong result – it’s hard to parse unpredictable HTML!

It sounds like browsers have tried to align on a standard for contenteditable HTML… my guess is that those efforts broke the innerText linebreak parsing without anyone noticing.

There’s a great history on MDN if you’d like more info.

A solution emerges

Ok, so what do we do for now if we need a plaintext value but also need perfect linebreaks?

Here is a snippet I’m using – it’s working but I haven’t tried to account for use cases beyond my own. So… apply it with your own critical thinking.

let newValue = '';
let isOnFreshLine = true;

// Recursive function to navigate childNodes and build linebreaks with text
function parseChildNodesForValueAndLines(childNodes: NodeListOf<ChildNode>) {
  for (let i = 0; i < childNodes.length; i++) {
    const childNode = childNodes[i];

    if (childNode.nodeName === 'BR') {
      // BRs are always line breaks which means the next loop is on a fresh line
      newValue += '\n';
      isOnFreshLine = true;
      continue;
    }

    // We may or may not need to create a new line
    if (childNode.nodeName === 'DIV' && isOnFreshLine === false) {
      // Divs create new lines for themselves if they aren't already on one
      newValue += '\n';
    }

    // Whether we created a new line or not, we'll use it for this content so the next loop will not be on a fresh line:
    isOnFreshLine = false;

    // Add the text content if this is a text node:
    if (childNode.nodeType === 3 && childNode.textContent) {
      newValue += childNode.textContent;
    }

    // If this node has children, get into them as well:
    parseChildNodesForValueAndLines(childNode.childNodes);
  }
}

// Parse the child nodes for HTML and newlines:
parseChildNodesForValueAndLines(e.currentTarget.childNodes);

// Do whatever you want with newValue now

You’ll also need `white-space: ‘pre-wrap’` set on your elements to preserve your linebreaks.

In testing that, I’ve another found issue in Safari where a single unbreakable word that’s too wide for its element will render with an extra incorrect linebreak – but not when contenteditable is set! But that’s an issue for another day.

I hope this experience helps someone else who stumbles upon this problem.

Raise the Skill Cap by Hinting at Upcoming Events

krate iphone game colors hud update

The HUD now displays the next 3 colors for the player.

One of my concerns with Krate is that I would end up with a game that came down to luck more than skill. One of my goals is to make the game re-playable because there’s potential for growth. I want a high “skill cap”, such that a talented player will end up with a dramatically better result than a casual player. One example of a low skill cap was the Paladin class in early WoW. They were very beginner friendly, with only a few buttons to press. The downside was that an experienced player couldn’t perform much better than a beginner. DOTA2 is another game with a mix of low skill cap (drow ranger) and high skill cap (meepo) heroes. With each decision I make, I try to think “will this raise or lower the skill cap?” because I believe a high skill cap gives an authentic reason for the player to replay the game.

Strategy through Information

I want the player to be actively planning and strategizing while playing Krate. Players have a chance to plan and react well or poorly to each game event. Players can better plan if I make sure to give plenty of information. One way I found to do this is to show the upcoming colors that the player will place. Originally, I was only showing the active color to be placed, and I found I wasn’t planning much, but simply reacting to each new color assignment. I built out a system to display the next 3 colors and I think it’s added a lot to the game’s strategy. Check out the video with the new color assignment HUD:

(more…)

Swift Events: Create Triggers and Listeners in Swift

Backbone includes an elegant and awesome events system that allows for some beautiful code organization in web apps. The events system lets you put your code where it belongs, rather than being tied in to a bunch of function calls in the event trigger. You can add and remove functionality from different events as well. It’s especially useful for game systems like keeping track of events that count towards achievements.

I wanted this functionality for Krate, my iPhone puzzle game, so I wrote a fast solution that mimics the most usable parts of Backbone’s system for custom Swift events triggering.

You can find it here: Swift Events on GitHub. There’s some examples and more information on using the class. Give it a try and let me know how you like it – it’s very usable right now, but I’m definitely open to feedback and improvements.

Krate Advancing by Leaps and Bounds

iPhone puzzle game krate beta graphics

Adding placeholder graphics to Krate makes it feel way more fun!

I am joining in the Summer of Swift fun with Krate. It’s a fitting event for me, as the timeframe fits my predetermined goals perfectly. Plus it enforces a regular update schedule which I can incorporate into Thinking Swiftly.

Krate has improved tremendously over the past few days. The game is already coming to life through new graphics and some easy juice like screen shakes. I’m using the excellent Kenney Asset Pack. Kenney is free to use, but I strongly recommend making a donation as it provides terrific placeholder art.

Quick Progress

I created a lot of the underlying systems for the game this week, including a Sound manager, an open source custom events manager for Swift, and the structure for keeping score and adjusting difficulty.

Also new is the creeping plague of darkness that gives the game a quick pace, and some placeholder particle effects to suggest tile placements. Phew! Lots of good stuff. I’ve included a video of an entire game in the full post.

(more…)

The New iPhone Puzzle Game: Introducing Krate

iphone puzzle game chess board

First, I wanted to build the game board using SKSpriteNodes. A good launching point for writing some game rules!

My favorite part of making games . . .

My favorite part of making games is looking back on early screenshots. I love to watch those early, blocky chunks of color transform into immersive game elements. In that spirit, I want to post some early screenshots of Krate, my new twisty, fun iPhone puzzle game.

Krate is my summer Swift learning project, born through the fortuitous coincidence of the Swift announcement with the Spring semester’s end. I often start overly ambitious game projects, doomed to drown by their own weight. No more! This time, I’m going to make an iPhone puzzle game. How hard can it be? 😉

Krate’s concept is simple; anyone who’s played any of the gem/jewel games can pick it up. That said, I wouldn’t be satisfied with a pure copy, so I spent some time thinking on unique mechanics. In this puzzle game, you start with a blank board and fill in tiles with a color randomly assigned on each turn. You can clear tiles any time you have 3 or more of the same color touching. That’s just the beginning, I’ve added more mechanics and compiled an early gameplay video.
(more…)