(*SKIP)(*FAIL) workaround in JavaScript RegExp

You can work around the missing (*SKIP)(*FAIL) support in JavaScript using capturing groups in the pattern and a bit of code logic.

Note the (*SKIP)(*FAIL) verb sequence is explained in my YT video called “Skipping matches in specific contexts (with SKIP & FAIL verbs)”. You can also find a demo of JavaScript lookarounds for four different scenarions: extracting, replacing, removing and splitting.

Let’s adjust the code for the current question. Let’s assume word always consists of word characters (digits, letters or underscores).

  1. Extracting: Capture the word into Group 1 and only extract Group 1 values:
const text = `foo <a href="https://stackoverflow.com/questions/71812772/foo.com">foo</a> foobar`;
const word = 'foo';
const regex = new RegExp(String.raw`<a .*?">|\b(${word})\b`, 'gi');
console.log(Array.from(text.matchAll(regex), x=>x[1]).filter(Boolean)); // => 1st word and `>foo<`
  1. Removing: Capture the context you need to keep into Group 1 and replace with a backreference to this group:
const text = `foo <a href="https://stackoverflow.com/questions/71812772/foo.com">foo</a> foobar`;
const word = 'foo';
const regex = new RegExp(String.raw`(<a .*?">)|\b${word}\b`, 'gi');
console.log(text.replace(regex, '$1')); // =>  <a href="https://stackoverflow.com/questions/71812772/foo.com"></a> foobar
  1. Replacing: Capture the context you need to keep into Group 1 and when it is used, replace with Group 1 value, else, replace with what you need in a callback function/arrow function used as the replacement argument:
const text = `foo <a href="https://stackoverflow.com/questions/71812772/foo.com">foo</a> foobar`;
const word = 'foo';
const regex = new RegExp(String.raw`(<a .*?">)|\b${word}\b`, 'gi');
console.log(text.replace(regex, (match, group1) => group1 || 'buz' ));
// => buz <a href="https://stackoverflow.com/questions/71812772/foo.com">buz</a> foobar
  1. Splitting: This is the most intricate scenario and it requires a bit more coding:
const text = `foo <a href="https://stackoverflow.com/questions/71812772/foo.com">foo</a> foobar`;
const word = 'foo';
const regex = new RegExp(String.raw`(<a .*?">)|\b${word}\b`, 'gi');

let m, res = [], offset = 0;
while (m = regex.exec(text)) { // If there is a match and...
  if (m[1] === undefined) {    // if Group 1 is not matched
    // put the substring to result array
    res.push(text.substring(offset, m.index)) // Put the value to array
    offset = m.index + m[0].length  // Set the new chunk start position
  }
}
if (offset < text.length) {     // If there is any more text after offset
  res.push(text.substr(offset)) // add it to the result array
}
console.log(res);
// => ["", " <a href=\"foo.com\">", "</a> foobar"]

Leave a Comment