How to reliably emphasize a letter in an HTML fragment via the DOM

I am creating a keyboard access library for my web application. To hint which keys to press, I want to underline the key in the links. For instance.

O rders --- I nvoices --- Ope n Incidents

For pure snippets of text, this is simple (jQuery is used here):

part.html(part.html().replace(new RegExp(key, 'i'), '<u>$&</u>'));

But it breaks terribly if partthere is any html markup inside . Is there an elegant way to just update text nodes and not markup?

Clarification: my use case is hundreds of server-side nested templates that generate HTML. Attributes accesskeyare currently being added manually. The result of something like <a href="angebote.html" accesskey="t"><i class="fa fa-fw fa-podcast"></i>Ange<b>bote</b></a>. Javascript Front-End Script then, among other things, adds key bindings and tries to emphasize the associated keys.

+4
source share
3 answers

To get only to text nodes and make a replacement there, you can use a pedestrian. I would also create a function that isolates the found letter in the element spanand returns this. Then you can use jQuery (or any other methods) to decorate this element as you like:

function getLetter(elem, letter){
    if (elem.get) elem = elem.get(0); // remove jQuery wrapper
    var nodes = document.createTreeWalker(elem, NodeFilter.SHOW_TEXT, null, null),
        $node, text, pos, $span;
    letter = letter.toLowerCase();
    while ($node = $(nodes.nextNode())) {
        text = $node.text();
        pos = text.toLowerCase().indexOf(letter);
        if (pos > -1) {
            $span = $('<span>').text(text[pos]);
            $node.replaceWith( // separate the letter from the other text
                text.substr(0, pos),
                $span,
                text.substr(pos+1)
            );
            return $span; // return the element that just has that one letter
        }
    }
}

getLetter($('#orders'), 'o').addClass('underline');
getLetter($('#invoices'), 'i').addClass('underline');
getLetter($('#incidents'), 'n').addClass('underline');
.underline { text-decoration: underline }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h3>Some mix of HTML:</h3>
<div id="orders">
    <span style="color: purple">Order<i>(s)</i></span>
</div>
<div id="invoices">
    <ul><li><b>!!</b>Invoices <i>(urgent)</i></li></ul>
</div>
<div id="incidents">
    <table style="border: 1px solid"><tr><td>Open</td><td>incidents</td></tr></table>
</div>
Run codeHide result

Function Explanation

:

  • elem: , , . HTML.
  • letter: : .

:

  • undefined, , , .
  • span, : . .
+2

API , .

let range = document.createRange();
let div = document.createElement("div")
div.textContent = "Orders --- Invoices --- Open Incidents"
let textNode = div.firstChild

let underline = document.createElement("u")

range.setStart(textNode, 0);
range.setEnd(textNode, 1);
range.surroundContents(underline);
console.log(div.outerHTML)
// <div><u>O</u>rders --- Invoices --- Open Incidents</div>
+1
[].forEach.call(document.getElementsByTagName('a'),function(el){  
     el.innerHTML=el.innerHTML.replace(el.getAttribute('acceskey'), 
     '<u>'+el.getAttribute('acceskey')+'</u>')  
 })

Try it here: JS FIddle

0
source

Source: https://habr.com/ru/post/1675974/


All Articles