Home > Blockchain >  JavaScript find string and highlight
JavaScript find string and highlight

Time:01-25

I'm having some issues with searching a string in long text. I want to extract only searched text and bold searched text with maybe 10-20 characters before it and after searched characters.

So basically what I want to achieve is, ex. from that text:

Hi there, I want to achieve a new goal to create a good search bar for my app. It should be just as any other search bar.

So if I want to search "good search", it should return something like:

...achieve a new goal to create a good search bar for my app. It should be just...

What would be a best way to do it? I tried something like:

 const text = "Hi there, I want to achieve a new goal to create a good search bar for my app. It should be just as any other search bar."
    const search_text = "good search"
    const radius = 10;
    // To determine where is the first character
    const find_first = text.search(search_text)
    const search_from = find_first - radius;

    // To ensure that we are taking from first with length of searched text and additional ones
    const search_to = find_first   search_text.length   radius

But still, this is only to determine how to check which characters to get. But how to list them and show with highlight?

CodePudding user response:

const text = "Hi there, I want to achieve a new goal to create a good search bar for my app. It should be just as any other search bar."
const search_text = "good search"
const RADIUS = 20;

const indexPosition = text.search(search_text)
const startPosition = indexPosition - RADIUS 
const endPosition = indexPosition   RADIUS   search_text.length
const searchResult = (`...${text.slice(startPosition, endPosition)}...`).replace(search_text, `<b>${search_text}</b>`)
console.log({searchResult})
  

and then you can apply your highlight with the way you like,(I add <b> tag around search text, I assume u want it as HTML format)

CodePudding user response:

This is assuming you want it for a webpage, you can search in an input and click the button to see the result:

const input = document.querySelectorAll('.searchInput')[0];
const button = document.querySelectorAll('.searchButton')[0];
const sourceText = document.querySelectorAll('.source')[0].textContent;
const result = document.querySelectorAll('.result')[0];
const radius = 10;

function resetResult() {
  result.innerHTML = '';
}

function handleNoResult() {
  const noResultMessage = document.createElement('p');

  noResultMessage.textContent = 'No results found';
  result.append(noResultMessage);
}

button.addEventListener('click', () => {
  resetResult();

  const search = input.value;
  const startSearchIndex = sourceText.indexOf(search);

  if (startSearchIndex === -1) {
    return handleNoResult();
  }

  const endSearchIndex = startSearchIndex   search.length;
  const lastIndex = sourceText.length - 1;
  const start = Math.max(0, startSearchIndex - radius);
  const end = Math.min(endSearchIndex   radius   1, lastIndex);
  const startText = document.createElement('span');
  const hightlightText = document.createElement('strong');
  const endText = document.createElement('span');

  if (start > 0) {
    startText.textContent = '...';
  }

  startText.textContent  = sourceText.slice(start, startSearchIndex);
  hightlightText.textContent = sourceText.slice(startSearchIndex, endSearchIndex);
  endText.textContent = sourceText.slice(endSearchIndex, end);

  if (end !== lastIndex) {
    endText.textContent  = '...';
  }

  result.appendChild(startText);
  result.appendChild(hightlightText);
  result.appendChild(endText);
});
<input type="text" >
<button >Search</button>
<h3>Text to Search</h3>
<div >
  It is a period of civil war. Rebel spaceships, striking from a hidden base, have won their first victory against the evil Galactic Empire. During the battle, Rebel spies managed to steal secret plans to the Empire’s ultimate weapon, the DEATH STAR, an armored space station with enough power to destroy an entire planet. Pursued by the Empire’s sinister agents, Princess Leia races home aboard her starship, custodian of the stolen plans that can save her people and restore freedom to the galaxy
</div>
<h3>Search Result</h3>
<div ></div>

CodePudding user response:

The "tricky part" of your requirement is to mutate the dom. All the previous answers already gives you an idea on how to find the string itself.

We need to store both the string previous to the ocurrence, the ocurrence itself and the rest. Notice that there's an extra padding of your choice

const firstPartOfContent = contentHTML.substring(0, ocurrenceIndex - PADDING);

const ocurrence = contentHTML.substring(
    ocurrenceIndex - PADDING,
    ocurrenceIndex   textToSearch.length   PADDING
);
        
const secondPartOfContent = contentHTML.substring(
    ocurrenceIndex   textToSearch.length   PADDING
);

Then we mutate the dom with

        
loremNode.innerHTML =
    firstPartOfContent   `<b>${ocurrence}</b>`   secondPartOfContent;
}

Example on codepen

CodePudding user response:

Look at the this website. Especially the part of JavaScript code which relates to highlighting (bold) the found text. Everything is well described there.

CodePudding user response:

The following snippet avoids breaking up words by taking up to 5 word-blank combinations (((?:\\S \\s){0,5})) before the search pattern pat and up to 5 blank-word combinations after the search pattern to create the formatted string result:

const pat="good";
const rx=new RegExp(`((?:\\S \\s){0,5})(${pat})(?=((?:\\s\\S ){0,5}))`,"g");
const res=[...document.querySelectorAll("p")].reduce((a,p)=>{
 let fnd=[...p.textContent.matchAll(rx)];
 if (fnd.length) a.push(fnd);
 return a;
},[]).flat();
document.querySelector("#result").innerHTML=res.map(([_,pre,txt,post])=>
 `<p>...${pre}<b>${txt}</b>${post}...`
).join("");
<p>Here we find some random text. It should fill the page but not attract any  unnecessary attention to itself if it is done in a good way.</p>
<p>In a second good paragraph I will hide the text that is of interest here: "Hi there, I want to achieve a new goal to create a good search bar for my good app. It should be just as any other search bar." Just to give it a bit more body this last sentence was also added.</p>
<hr>
<div id="result"></div>

The positive lookahead (?=((?:\\s\\S ){0,5})) after the search pattern is necessary for overlapping result strings. The second "good" in: "... to create a good search bar in my good app." would not be matched otherwise.

For simplicity I used <b>...</b> to format the text matching the search pattern bold. This can, of course, be replaced by anything else like <span >...</span>.

The new RegExp() expression will probably also need some further attention: should there be "special characters" (like a dot: . or a question mark ?) in the search pattern pat and these are to be found as such, they will need to be masked (by a preceding \\) as otherwise they will be misinterpreted as being part of a regular expression.

  • Related