Getting Started with CSS Custom Highlighting API

img

Text highlighting is a feature that can be detected on several sites and serves a variety of purposes. For example, browsers highlight elements found using the search function, and text editors and tools such as Concept or Grammar highlight spelling and grammatical errors.

Text highlighting enhances user experience, touchpad on laptop not working? Four easy fixes but adding this functionality to a website or app can be difficult. Fortunately, this will be much easier thanks to the CSS Custom Highlighting API, which allows us to easily style ranges of text. As of this writing, this feature is still in a working draft, so it's subject to change.

In this article, I'll give an overview of what the API does and things to consider about behavior and accessibility. I'll also share a few examples to illustrate the use of the CSS Custom Highlighting API in various use cases.

Jump ahead:

  • Learn the BasicsCreate a HighlightStyleSet a RangeApply a Highlight
  • Create and highlight more complex ranges
  • Handling Accessibility Limitations
  • CSS Custom Highlight API Example Select and Highlight Web Text Search in Page Components Order This Phrase Apply


Learn the basics

Using CSS to customize the highlighting API is a three-step process: What if my laptop is slow? Ten Ways to Improve Your Laptop's Performance Create styles in a style sheet, set highlight ranges, and then apply those styles to the ranges.

Before diving into the steps, I want to reiterate that the CSS Custom Highlighting API is still a working draft, which means it is still subject to change. Also, this feature doesn't currently work with Firefox, so if your project requires Firefox, you shouldn't be using it for production right now.

Having said that, let's start with the first step.

Create highlight styles

How does this CSS custom highlight API require me to use skype? How to make calls and send messages on Skype? We use pseudo-elements to set the styles we need. The W3C working draft defines this pseudo-element as "the portion of the document that is contained or partially contained within all scopes for which custom highlighting has been registered". ::highlight()

We need to add a name to the custom highlight created inside curly braces, so the CSS declaration for the custom highlight should look like this:

::highlight(sample-highlight) {
  background-color: rebeccapurple;
  color: whitesmoke;
}

You might be wondering if there are restrictions in the style rules that can be applied to this highlighting, and the answer is yes! The CSS rules at our disposal are very limited. To be more precise, we only use the following rules for quick lock windows 10/11 computer tutorial quick lock screen computer shortcuts , as they apply to pseudo-elements:::selection

  • background-color
  • caret-color
  • color
  • cursor
  • fill
  • stroke
  • stroke-width
  • text-decoration

</ul>

These rules were chosen because the browser can effectively apply them without recreating the site's layout or inserting new DOM elements (i.e. we can recreate this functionality by wrapping elements with inline HTML tags, for example) . span

To compare the performance difference between the CSS custom highlighting API and the traditional text formatting method of inserting spans, see this performance comparison demo by Fernando Fiori .

Setting range

After creating the styles, the next step is to set the scope using CSS. First, we'll define our text range using the constructor; this will allow us to select fragments of the document. This fragment can contain a node or part of a text node. Range()

The type of element that the constructor can choose is very important, because how to delete the system service? How to view all services installed on the computer? Because this means that to get the piece of text we want to use, we need to use a function that returns one of these elements. Range()

Let's consider the following text example:

<p>This example has <em>cursive</em> and <strong>bold</strong> text</p>

Now, let's create the styles:

:root::highlight(example) {
  background-color: rebeccapurple;
  color: whitesmoke;
}

If we try to create a range using this element, each node or piece of text will be a new range. p

We can use the and method to select the portion of text to be highlighted. range.setStartrange.setEnd

For this example text, each new element will be a new range, so we'll provide the following:

img

This example sentence shows where each range begins. "This example has " at position 0,  everything inside the element at position 1, " and " at position 2,  everything inside the element at position 3, and "text" at position 4.

Suppose we want to highlight the text "cursive and strong" in the sentence above. In this case, the method should be . You might think that the method should be, but that's not quite right! setStart1setEnd3

If we set start of range to , the API will start selecting it from the very beginning of the element, and if we set end range to , the selection will end at the beginning of the third element. So, to select all text inside an element, the attribute for this example should be . 13 strongrange.setEnd4

The and method takes two parameters: the DOM element we want to use and its position. Here's how it looks in our code: setStartsetEnd

const paragraph = document.querySelector("p")

const exampleRange = new Range(); exampleRange.setStart(paragraph, 1); exampleRange.setEnd(paragraph, 4); </code></pre>

Apply highlighting

Now that we have the range we want, it's time to highlight it! To do this, we need to create a new constructor that will receive our range as a property: Highlight()

const highlight = new Highlight(exampleRange)

Next, we need to call the method. It takes two properties: the class we created in the stylesheet and the new class we created earlier. This is what it looks like in our code: CSS.highlight.sethighlighthighlight

CSS.highlights.set("example", highlight);

But what if you need to clear the highlighting you created?

Just call that method and the highlighting will clear without issue. CSS. highlights. clear()

Create and highlight more complex ranges

The technique we demonstrated earlier for creating highlighted ranges works well when we want to select complete words, but what happens when we only need to select part of a word? Let's go back to our example and this time let's try to highlight only the letters "old tex".

First, let's create our style for this highlight:

:root::highlight(partial) {
  background-color: goldenrod;
  color: black;
}

Ok, now how do we avoid selecting the entire fragment?

My approach to this problem is to take a closer look at how the element (in this case the entire element) is made. First, I inspected it in more detail using the console for inspecting the property list. I then looked at the properties and it turned out to be this: pconsole.dir() childNodes

img

The console.dir of the p element. This property returns an array of elements within the paragraph. In this case, the second and fourth elements appear as their respective element labels, and the other elements appear as text.

The text "old tex" that we want to highlight is inside the element. To start our scope from this text, we can use , but this selects the entire element. So, how do we select only part of an element? strongparagraph. childNodes[3]

Either an element node or a text node will be selected, as before. In our case, we need to check inside this element for something that returns a text node. Range

Let's go back to ours, since it has a property we can use: console.dir() firstChild

img

Strong element of Console.dir. Attributes in this element return text nodes that can be used in constructors.

So now we have . which will return our string as the prototype. paragraph.childNodes[3] firstChildText

To set our starting position, we need to check where the "o" in "old" is. In this example, it's the second character, so if we split it into an array, the "o" would be the position. So when we set the method for the range, it would look like this: 1setStart

partialRange.setStart(paragraph.childNodes[3].firstChild, 1)

great! Now, let's set the end of this range.

The last character we want to highlight is "tex". As you will notice in the attribute list above, the last element has a type, so no need to access the attribute. childNodeschildNodestextfirstChild

Next, we need to check where to end our range; we'll use that method as follows: setEnd

partialRange.setEnd(paragraph.childNodes[4], 4)

Now, all we have to do is create the highlights:

const partialHighlight = new Highlight(partialRange);

CSS.highlights.set("partial", partialHighlight) </code></pre>

Here's what the highlight looks like:

There's a reason I'm showing both examples in the same CodePen -- it's important to consider how the browser will prioritize what to render when multiple styles are applied to the same string.

For example, the string "old" in "bold" has two styles. Which style will be displayed will depend on where we decide to call the method. CSS.highlights.set

In this case, I'm calling the method that first adds the style, and then the method that adds the style. Since this is JavaScript, the last method declared will take precedence. So in this case the "old" string will have the style applied. examplepartialpartial

Finally, as you might think, the method can be difficult to fully understand. This approach can start to fall short when you start thinking about scale. We'll look at some examples later in this article that use other methods for more complex use cases. But first, we need to consider an important consideration: accessibility. Range()

Handling Accessibility Limitations

The CSS custom highlighting API is very flexible, but what happens when you need to expose highlighting to assistive technologies like screen readers? This API covers this, but right now, it's limited to a few use cases.

Let's start with the basics. When creating a highlight using the constructor, there is a property called . This attribute provides semantic meaning to the highlight and defines how it will be exposed to screen readers and other assistive technologies! Highlight() type

The types we can use are fairly limited to the following three:

  • highlight: default assignment
  • spelling-error: Used to highlight misspelled content
  • grammar-error: used to highlight grammatically incorrect content

</ul>

If the highlight you're using has one of these types, you should definitely add that. To do this, just change the property like this:

// Creating a highlight with a new range
const highlight = new Highlight(range)

// Changing the attribute type of this range highlight.type = "spelling-error"</code></pre>

Why only these three types? According to the W3C specification, this is because these are considered the most common use cases for this API:

This initial set of types was chosen because they are expected to be a popular use case for highlighting APIs, and there is currently some existing support for expressing their semantics in platform accessibility APIs.

The spec also mentions that "UAs (User Agents) should provide custom highlighting for assistive technologies", and now it mostly does.

Here, I want to emphasize again that the CSS custom highlighting API is still a work in progress. I did some testing with NVDA and Chrome and found that it recognizes spelling or grammar errors before the text is highlighted, but when the text type is .highlight

Why is this important? Well, if this highlighting isn't just for visual purposes, but requires exposure to assistive technology, you might want to consider another option for the job. One solution that I think works is the one that ProgrammingTT demonstrates in this video, where the highlighted content is wrapped in an element.

At the time of writing, only two types of highlighting are exposed to assistive technologies, but the possibility of adding more highlighting types is open, as mentioned by the W3C:

The Accessibility API is currently unable to express the specific semantics of other expected highlighting API use cases. More types can be added in the future as the Accessibility API gains support for other popular use cases of the Express Highlighting API. HighlightType

Now, having said that, let's look at some use cases for this API!

CSS Custom Highlight API Example

To get a better idea of ​​what this API can do, let's take a look at some implementations of this API by the Microsoft Edge team, as well as an example I built. These use cases will also let us see different ways of dealing with ranges of text.

Select and highlight web text

Let's start with the example in the Microsoft Edge video "Highlighting text on the web with the CSS Custom Highlighting API." In this project, you can select text, highlight it to store it in a list, and check it later. The stored text will be highlighted using the CSS custom highlighting API.

For this example, Patrick Brosset uses the method to create a range with the following selection: document.getSelection()

const selection = document.getSelection();
const range = selection.getRangeAt(0);

Since any method that returns a text fragment or a node fragment is valid, this will do the trick. Next, Brosset starts using the method to add ranges and then highlight them as before. document.getSelection()Highlight

Since this video doesn't have a live demo, I decided to recreate the feature that highlights the selection. This is very similar to our previous example, with one key difference: this time we may need to highlight multiple ranges. To do this, let's first create some global variables:

let SELECTION_TEXTS = []
let selectionHighlighter = new Highlight()

The first variable is an array that will store each range we want to highlight, and the second variable creates an instance of this class. Highlight()

Why are we dealing with this on a global scale? Because we'll be adding scopes using the methods of this class, rather than using the syntax we used earlier.

Now, it's time to create our function to highlight these text ranges:

const textHighlight = () => {
  const selection = document.getSelection();
  const range = selection.getRangeAt(0)
  SELECTION_TEXTS.push(range)

SELECTION_TEXTS.forEach((selection) => {  selectionHighlighter.add(selection)  })  CSS.highlights.set("example", selectionHighlighter) }; </code></pre>

This is a little different than what we've seen before. Before we can add anything to our array, we must first add the new range to our array. We then need to iterate over it with methods to start adding selection ranges to methods. Next, we just need to call the method as before. selectionHighlighterforEachselectionHighlighterHighlight.add()CSS.highlights.set()

But what happens if we need to clear the highlights? For this we can use the following function:

const clearHighlight = () => {
  selectionHighlighter.clear()
  SELECTION_TEXTS = []
  CSS.highlights.clear()
}

Similar to , we can use to remove all highlighted elements in a global variable. Then, we just need to clear the global variables used for the array, and use the method we demonstrated earlier. Highlight.add()Highlight.clear()CSS.highlights.clear()

To see how it works, check out this demo:

I chose this example because I wanted to show how to use other strategies to select the desired range of text. In this case, works, but what happens when you need to create something larger? This is where our second use case comes in! document. getSelection()

Search in page components

The next demo, for the in-page search component, was also made by the Microsoft Edge team. It demonstrates how to use a to get all the nodes within a DOM element (in this case, an element) to create text nodes on a larger scale. As for the CSS custom highlighting API, we need all text nodes, so we'll use that instance. TreeWalkermainNodeFilter. SHOW_TEXT

Next, each node is checked using the method, and they are stored in a variable, with the result: TreeWalkerTreeWalker.nextNode()allTextNodes

img

Console screenshot of the variable allTextNodes; an array containing 37 elements, all of which are of type text.

This process gives us an array of text elements; we can add their names by adding as. Then, just add the range of text nodes, find those results, and highlight them as we did earlier with this method. This approach illustrates how we can use a to help us reliably inspect text nodes on a larger scale. inputCSS.highlights.set()TreeWalker

This demo would greatly benefit from the additional semantic implications of assistive technology highlights. Remember, there are only three types of this API, and highlighting search results is not one of them! So W3C, if you see this, including this semantic meaning of search results will definitely come in handy!

Now, let's look at the example I built; it effectively uses the semantic meaning available!

order this phrase app

My project idea is to create an application where the user receives a scrambled phrase and then needs to provide the correctly sorted phrase as an answer. Users will receive visual feedback via highlighting, depending on whether the words in the submission are in the correct order. I chose this example because it can take advantage of highlighting semantic types (in this case) to improve the accessibility of the application! grammar-error

img

Example of an "Order This Phrase" app. The phrase "Hi! I'm Danielle, nice to meet you!" is in the wrong order. There is an input field to provide the correct answer and a "Check your answer" button.

First, we need to create our highlight class! If we only relied on the background color to highlight correct and incorrect answers, the results would be hard to distinguish for colorblind users. Instead, I decided to use a strikethrough (or) for incorrect answers: line-through

:root::highlight(wrong-highlight) {
  background-color: crimson;
  color: whitesmoke;
  text-decoration-line: line-through;
  text-decoration-thickness: 2px;  
}

:root::highlight(right-highlight) {  background-color: forestgreen;  color: whitesmoke; } </code></pre>

As mentioned earlier, the key to creating semantic meaning is to add attributes to the desired highlighting classes. In this case, I only want to highlight those words that appear in the wrong order, because those words will generate syntax errors, so this is what I do when I create the instance globally. Since I had to declare empty arrays for highlighting multiple ranges, I decided to add these globally as well: typeHighlight()

let RIGHT_RANGES = [];
let WRONG_RANGES = [];

const rightHighlight = new Highlight();

const wrongHighlight = new Highlight(); wrongHighlight.type = "grammar-error"; </code></pre>

After several validations to check that the added words agree with the words in the answer, it's time to start adding our ranges! I decided to select the container of answers (in this case ) and check each of its children. Since each inner contains only this element, the range starts from, and ends with. olli01

With that in mind, here's the code I use:

const answerListNodes = ANSWER_LIST.children;

// Selects the range of each <li> to start the highlighting process answer.forEach((word, index) => {  const wordRange = new Range();  wordRange.setStart(answerListNodes[index], 0);  wordRange.setEnd(answerListNodes[index], 1);

if (word === RIGHT_ORDER[index]) {  // If the answer is right  RIGHT_RANGES.push(wordRange);  } else {  // If the answer is wrong  WRONG_RANGES.push(wordRange);  } }); </code></pre>

Next, we can use the and array to add selected ranges to our instance, then highlight them on screen using this method: RIGHT_RANGESWRONG_RANGESHighlight() CSS.highlights.set()

// Highlights the answers in the right position
RIGHT_RANGES.forEach((element) => {
  rightHighlight.add(element);
});

CSS.highlights.set("right-highlight", rightHighlight);

// Highlights the answers in the right position WRONG_RANGES.forEach((element) => {  wrongHighlight.add(element); });

CSS.highlights.set("wrong-highlight", wrongHighlight); </code></pre>

If you want to see how it works, check out this CodePen:

Again, I used this approach because the CSS custom highlighting API provides a semantic type that is perfect for this example.

epilogue

The CSS Custom Highlighting API is a new feature that offers some interesting solutions to some of the usability and accessibility-related issues we face. It also offers superior performance compared to other options. This API can be a little tricky when you think about how to choose a range, but once you figure that out, the tool opens up a whole new set of possibilities!

This API is still a work in progress and currently lacks global support, but I think it has a lot of potential. I hope that in the future this API will expand its browser support and also support more semantic types that can be used with assistive technologies.

Guess you like

Origin blog.csdn.net/weixin_47967031/article/details/130167939