Blueprint by Tiny
Return to Tiny.cloud
Return to Tiny.cloudTry TinyMCE for Free
Search by
Abstract image of browser tab and content.

A quick guide to browser selection models

Team Tiny

February 20th, 2017

Written by

Team Tiny
Team Tiny

Category

Engineering

Tagged

One of the complexities of writing a web-based text editor is accounting for differences between browser selection APIs. This has been the topic of recent discussions in WordPress #core-editor, in the context of backward compatibility for project gutenberg.

Looking at the comparison of what is supported by various browsers, it is clear that support for browsers prior to Edge is challenging from a programmatic perspective. This is likely why Medium made the call to only support browsers post IE11.

* We have experienced intermittent failures in our testing

Range API

When dealing with different browsers, the implementations of the selection model can be subtly different. Internet Explorer, for example, can be significantly different depending on the version.

Most modern browsers support what is called the Range API specificationA Range is a left-to-right selection from (startContainer, startOffset) -> (endContainer, endOffset) within a document. How a visual selection maps to a range also differs slightly across browsers.

In a range, a container is an HTML node and the offset is a number. The combination of the two depends on what the node is. The following rules provide a reasonable approximation of how it works across modern browsers.

  • For a text node, the offset is the character position. The cursor position before the first character in the text node is 0 and the offset after the last character in the text node is the length of the text (e.g. 3 for “cat”)
  • For an element node, the offset is the child index. For an element with three child nodes, an offset of 0 is before the first child and the offset of 3 is after the last child. An offset of 1 is visually after the first child and before the second

Browsers can choose to report these ranges from different levels. For example, in the HTML segment <p>I <span>am</span> in</p>, a cursor that looks to be positioned before the “a” in am could have several different positions programmatically, depending on the browser model.

If the user was before the “a” in “am”, then the start container could potentially be (without a visual difference):

  1. startContainer = p and startOffset = 1
  2. startContainer = span and startOffset = 0
  3. startContainer = “am” and startOffset = 0

When handling these differences, some editors choose to normalise the Range in order to make the output consistent. TinyMCE takes this approach.

Selection Direction (IE)

Another important difference is the selection direction. Recall from above that ranges are left-to-right. This becomes very important when keyboard navigation is used. Because a range is left-to-right, it cannot represent a selection that is right-to-left. People use right-to-left selections all the time without realising it, and expect them to work.

Let’s try an experiment that doesn’t even require an editor:

  1. Locate a paragraph in the middle of any web page content. For this example, we will use the second paragraph of https://www.tinymce.com/docs/get-started-cloud/editor-and-features/.
  2. Use the mouse to highlight from the start (You may use Tiny Cloud…) to the end of the paragraph
  3. Press Shift + Down. Does the selection shrink or expand? What is now selected?
  4. Press Shift + Up. Does the selection shrink or expand? What is now selected?
  5. Clear your selection
  6. Now, highlight the same paragraph but this time start dragging from the end (…into your application) to the start of the paragraph
  7. Press Shift + Down. Does the selection shrink or expand? What is now selected?
  8. Press Shift + Up. Does the selection shrink or expand? What is now selected?

Why do they behave differently? They have a different selection direction. The selection from step (2) is left-to-right and the selection from step (6) is right-to-left. This means that Shift + Down and Shift + Up have different behaviors.

When making selections like this, the starting point of the selection is called the (anchorNode, anchorOffset) and the ending point of the selection is the (focusNode, focusOffset). The Shift + Down or Shift + Up moves the (focusNode, focusOffset) which is why you get the different behavior.

Now, because ranges do not support right-to-left selections, the Range API is not sufficient for programmatically setting a right-to-left selection. Instead, developers need to use the extend methodUnfortunately, IE does not support the extend method. It supports backward selection through user action, but a developer can not programmatically create a backwards selection.

This is a very important distinction in situations where the developer needs to save a selection, do some action and restore it. If the previous selection was backwards, then they cannot restore it and preserve the same selection direction.

An example of where this is a problem is with spelling highlights. If the content is being annotated with span tags, and those span tags might break the existing selection, then the developer needs to restore the selection after applying the annotations. However, if this is happening in the background it can break user expectation. In a scenario where the user is pressing Shift + Up and expecting the selection to grow from the top, the direction is lost and Shift + Up starts to shrink from the end of the selection as soon as it sets programmatically.

Multiple ranges

Firefox is the only current browser that supports multiple non-contiguous ranges. This occurs in two main situations:

  1. The user has used Ctrl or Cmd to add an additional selection manually
  2. The user has clicked on cells while holding Ctrl or Cmd + Shift and Firefox has added the entire cell to the selection

The consequence of not supporting multiple ranges is that on other browsers, a user cannot have multiple native selections on the same document. However, most browsers (not IE) support having at most one selection per document (and many selections on Firefox). Additional documents are created with iframes.

Separate iFrame Selection

Every browser that is not Internet Explorer supports having a separate selection model inside each iframe. This allows users to have selections inside the iframe without losing their selection in the outer document. This is particularly important with rich text editing.

When this isn’t supported by the browser, the developer of the editor needs to simulate it by creating a cache of the selection and storing it somewhere. Then, when the editor selection is required, they can use that cache if the editor selection has been lost in the meantime.

Edge appears to support having separate selections in iframes but at various points, it hasn’t proved to be reliable. Edge’s selection, in general, is still a bit problematic.

Legacy Selection (IE < 9)

There are two sets of selections on older IE versions (API reference). These are TextRange and ControlRange. These are very limited since they are not exposing the actual offset position within a text node.

For TextRange, you can move the selection character by character, i.e. you can move around a invisible selection marker and compute the text offset by checking if you passed that marker or not. This method is not very efficient and has a lot of edge cases.

The control selection is a collection with elements currently selected. This selection type is used when a user is selecting an image or a table element where there is not caret present but there is still a selection.

The older IE versions also do not have a DOM Range API so operations on ranges are limited unless you use a polyfill or feature fill. There are third party polyfills, but these suffer from performance issues on medium to large documents.

I also wanted to give a big thanks and shout out to Morgan Smith and Johan “Spocke” Sörlin for helping me put this guide together. Have thoughts on the post? Let us know your comments below. Don’t forget to follow us on @joinTiny for more.

Related references

Complex selection interactions to be mindful of when coding editors (working document)

Design
Team Tiny
byTeam Tiny

Powering more than 40% of the world’s websites.

Related Articles

  • Monitor, keyboard, and mug.
    Engineering

    2020 developer trends for software developers and programmers

    by Ben Long in Engineering
Subscribe for the latest insights served straight to your inbox. Delivered weekly.

Deploy TinyMCE in just 6 lines of code

Built to scale. Developed in open source. Designed to innovate.

Begin with your FREE API Key
Tiny Editor