Select a substring in an element

What is the best way to highlight a specific substring in a div element in an event? By emphasizing, I mean applying some CSS style, like a yellow background or something else.

Basically, I need a simple JS client form function:

function (element, start, end) { // element is the element to manipulate. a div or span will do // start and end are the start and end positions within the text of that // element where highlighting should be. // do the stuff } 

Only one backlight will be active.

+4
source share
5 answers

You will need to wrap the text you want in your own <span> tag so that you can give your text its own style. Using the requested function definition, you can do this as follows:

 function (element, start, end) { var str = element.innerHTML; str = str.substr(0, start) + '<span class="hilite">' + str.substr(start, end - start + 1) + '</span>' + str.substr(end + 1); element.innerHTML = str; } 

You can then define CSS for the hilite class to control the style of this text.

 .hilite {color: yellow;} 

This assumes that the beginning and the end are the indices in innerHTML of the first and last characters that you want to highlight.

If you want to be able to name it again on the same element (to move the shortcut around), you can do it like this:

 function (element, start, end) { var item = $(element); var str = item.data("origHTML"); if (!str) { str = item.html(); item.data("origHTML", str); } str = str.substr(0, start) + '<span class="hilite">' + str.substr(start, end - start + 1) + '</span>' + str.substr(end + 1); item.html(str); } 
+4
source

Why using the backlight self-protection feature is a bad idea

The reason it’s probably a bad idea to start creating your own highlighting function from scratch is because you are likely to run into problems that others have already solved. Tasks:

  • You will need to remove the text nodes with HTML elements in order to highlight your matches without destroying the DOM events and starting the DOM regeneration again and again (which would be, for example, using innerHTML )
  • If you want to delete the selected elements, you will need to delete the HTML elements with their contents, as well as combine the separated text nodes for further search. This is necessary because each highlighter plugin searches inside text nodes for matches, and if your keywords are divided into several text nodes, they will not be found.
  • You will also need to create tests to make sure your plugin works in situations you have not thought about. And I'm talking about cross-browser tests.

Sounds complicated? If you need some features, such as ignoring some highlight elements, diacritical mapping, matching synonyms, searching inside an iframe, split search by words, etc., this becomes more and more complicated.

Use existing plugin

When using an existing, well-implemented plugin, you don’t need to worry about the above things. Article 10 of jQuery plugins for text slides on sites compares popular backlit plugins.

Take a look at mark.js

mark.js is a plugin that is written in pure JavaScript but is also available as a jQuery plugin. It was designed to offer more features than other settings plugins:

  • search for keywords separately, not the full term
  • map diacritics (For example, if "justo" should also match "justò")
  • ignore matches inside custom elements
  • use custom highlight element
  • use custom highlight class
  • display of user synonyms
  • search inside inside iframe
  • get conditions not found

Demo

Alternatively, you can see this script .

Usage example :

 // Highlight "keyword" in the specified context $(".context").mark("keyword"); // Highlight the custom regular expression in the specified context $(".context").markRegExp(/Lorem/gmi); 

This is a free and developed open-source on GitHub ( link to the project ).

+4
source

If you don't have events connected or complex HTML, you can simply search and replace with HTML:

 element.innerHTML = element.innerHTML.replace(/search/gi, function(match) { return '<span class="highlight">' + match + '</span>' }); 

If you want something better, you can directly manipulate the DOM without using innerHTML , which will save events and work for more complex HTML:

 /* * Takes in an array of consecutive TextNodes and returns a document fragment with `word` highlighted */ function highlight_text_nodes($nodes, word) { if (!$nodes.length) { return; } var text = ''; // Concatenate the consecutive nodes to get the actual text for (var i = 0; i < $nodes.length; i++) { text += $nodes[i].textContent; } var $fragment = document.createDocumentFragment(); while (true) { // Tweak this if you want to change the highlighting behavior var index = text.toLowerCase().indexOf(word.toLowerCase()); if (index === -1) { break; } // Split the text into [before, match, after] var before = text.slice(0, index); var match = text.slice(index, index + word.length); text = text.slice(index + word.length); // Create the <mark> var $mark = document.createElement('mark'); $mark.className = 'found'; $mark.appendChild(document.createTextNode(match)); // Append it to the fragment $fragment.appendChild(document.createTextNode(before)); $fragment.appendChild($mark); } // If we have leftover text, just append it to the end if (text.length) { $fragment.appendChild(document.createTextNode(text)); } // Replace the nodes with the fragment $nodes[0].parentNode.insertBefore($fragment, $nodes[0]); for (var i = 0; i < $nodes.length; i++) { var $node = $nodes[$nodes.length - i - 1]; $node.parentNode.removeChild($node); } } /* * Highlights all instances of `word` in `$node` and its children */ function highlight($node, word) { var $children = $node.childNodes; var $current_run = []; for (var i = 0; i < $children.length; i++) { var $child = $children[i]; if ($child.nodeType === Node.TEXT_NODE) { // Keep track of consecutive text nodes $current_run.push($child); } else { // If we hit a regular element, highlight what we have and start over highlight_text_nodes($current_run, word); $current_run = []; // Ignore text inside of our <mark>s if ($child.nodeType === Node.ELEMENT_NODE && $child.className !== 'found') { highlight($child, word); } } } // Just in case we have only text nodes as children if ($current_run.length) { highlight_text_nodes($current_run, word); } } /* * Removes all highlighted <mark>s from the given node */ function unhighlight($node) { var $marks = [].slice.call($node.querySelectorAll('mark.found')); for (var i = 0; i < $marks.length; i++) { var $mark = $marks[i]; // Replace each <mark> with just a text node of its contents $mark.parentNode.replaceChild(document.createTextNode($mark.childNodes[0].textContent), $mark); } } 

Demo: https://jsfiddle.net/wLkbbo5m/4/

If you want even more features, just use the library (e.g. mark.js ). It makes no sense to reinvent the wheel.

+3
source

Try the Highlighter.js library here: https://gist.github.com/wliwanag/03d95916c7ba5d17e226

It supports: Highlighting text in child elements. Use the words in the text as a search key (for example, the example below will highlight "simple", "1500", etc.).

For use:

 var searchInput = "simply 1500 bee y"; var textSource = $("#text-container").html(); var htmlSource = $("#html-container").html(); $("#text-result").html(highlightHtml(textSource, searchInput)); $("#html-result").html(highlightHtml(htmlSource, searchInput)); 

Fiddle

0
source

If you want a specific text between start / end to be replaced (instead of each occurrence), it would look something like this:

 function(element, start, end) { textToHilight = element.innerHTML.substr(start, end); element.innerHTML = element.innerHTML.substring(0, start) + "<span class='hilight'>" + textToHilight + "</span>" + element.innerHTML.substring(end); } 
-one
source

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


All Articles