3

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?

5
  • 1
    Could you elaborate on what you mean by "Show with highlight". Is this a web page? An app? A server response? Commented Jan 24, 2023 at 21:57
  • Show your HTML or where you want to display the highlighted text Commented Jan 24, 2023 at 21:57
  • Put something like <span class="highlight">...</span> around the highlighted text. Then use CSS to specify how this class should be displayed. Commented Jan 24, 2023 at 21:59
  • Well it's not about highlighting. It's more like how to get the text Commented Jan 24, 2023 at 22:00
  • I think you can edit your title, to be more clear about what your question actually is Commented Jan 24, 2023 at 22:06

4 Answers 4

3

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)

Sign up to request clarification or add additional context in comments.

1 Comment

With this approach only the first occurrence of a search pattern will be found in case of repeating patterns. Word boundaries will also not be respected.
2

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 inp = document.querySelector("input"),
  findPat = () => {
    let html, pat = inp.value.trim();
    if (pat.length) {
      const rx = new RegExp(`(?<=((?:\\S+\\s+){0,5}))\\b(${pat})\\b(?=((?:\\s+\\S+){0,5}))`, "ig");
      html = [...document.querySelectorAll("#content p")].reduce((a, p) => {
        let fnd = [...p.textContent.matchAll(rx)];
        if (fnd.length) a.push(fnd);
        return a;
      }, []).flat().map(([_, pre, txt, post]) =>
        `<p>...${pre}<b>${txt}</b>${post}...`
      ).join("");
    } else html = "";
    document.querySelector("#result").innerHTML = html
  };
inp.addEventListener("input", findPat); // assign the event function to the "input" event
findPat(); // run the event function with the initial value of inp
<div id="content">
  <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>
</div>
<input type="text" value="good"></input>
<hr>
<div id="result" value="good"></div>

The positive lookbehind ((?<=((?:\\S+\\s+){0,5}))) and lookahead (?=((?:\\s+\\S+){0,5})) around the search pattern (\\b(${pat})\\b -> the \\b look for a word boundary) are necessary for allowing fully overlapping result strings. The second "good" in: "... to create a good search bar in my good app." could 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 class="hilight">...</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.

Comments

0

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" class="searchInput">
<button class="searchButton">Search</button>
<h3>Text to Search</h3>
<div class="source">
  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 class="result"></div>

2 Comments

I don't know if there is a way to return all searches that match, take for instance the word "a".
There are a few ways you could go about that, probably creating a recursive function to see continue checking after each match, starting from the last end index until a match it not found would be the best way to go. If he says he needs help with that I can assist.
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.