突出显示 JavaScript 中子字符串中的子字符串

Highlight substring within substring in JavaScript

我试图使用开始和结束索引突出显示字符串中的子字符串。但是我在突出显示重叠的子字符串时遇到问题。 例如

我有一个 text 和一个 array 用于突出显示。

有什么办法,怎样才能避免标签重叠。 我尝试使用 regex 匹配子字符串,但无法解决它。

sandbox link

let tagObject = {
  "text": "I am asking a question in Whosebug",
  "tags": [{
      start: 0,
      end: 1,
      value: "I",
      tag: "TAG1"
    },
    {
      start: 0,
      end: 4,
      value: "I am",
      tag: "TAG2"
    },
    {
      start: 5,
      end: 22,
      value: "asking a question",
      tag: "TAG3"
    },
    {
      start: 14,
      end: 22,
      value: "question",
      tag: "TAG4"
    },
    {
      start: 14,
      end: 25,
      value: "question in",
      tag: "TAG5"
    }
  ]
}

const color = {
  outer: "color: #1d39c4; background: #f0f5ff; border-color: #adc6ff;",
  inner: "background: #adc6ff;",
  bg: "#f0f5ff"
}

const createStyledString = (obj) => {
  let {
    text,
    tags
  } = obj;
  tags.sort((a, b) => b.start - a.start);
  tags.forEach(t => {
    const {
      start,
      end,
      value,
      tag
    } = t;
    text = text.substring(0, start) +
      `<span contenteditable="false" data-action="${value}---${start}---${end}---${color.bg}---${tag}" unselectable="on" onselectstart="return false;" name="tag" class="outer" style="${color.outer}">${value}<span class="ne-c-inner" unselectable="on" onselectstart="return false;" data-action="${value}---${start}---${end}---${color.bg}---${tag}" style="${color.inner}">${tag}</span></span>` +
      text.substring(end)
  });
  return text;
}
document.body.innerHTML=createStyledString(tagObject)

您的方法存在两个主要问题。

  1. 首先,如果您有嵌套值,那么您将多次附加它们。
  2. 其次,您将开始和结束位置视为在修改字符串后它们没有改变。

因此可能的解决方案如下:

  1. 不要嵌套值,只需再次获取相同的子字符串即可。
  2. 跟踪真实位置以及初始“虚拟”位置,并使用它在正确的位置插入代码。

这是一个工作片段。

let tagObject = {
  "text": "I am asking a question in Whosebug",
  "tags": [{
      start: 0,
      end: 1,
      value: "I",
      tag: "TAG1"
    },
    {
      start: 0,
      end: 4,
      value: "I am",
      tag: "TAG2"
    },
    {
      start: 5,
      end: 22,
      value: "asking a question",
      tag: "TAG3"
    },
    {
      start: 14,
      end: 22,
      value: "question",
      tag: "TAG4"
    },
    {
      start: 14,
      end: 25,
      value: "question in",
      tag: "TAG5"
    }
  ]
}

const color = {
  outer: "color: #1d39c4; background: #f0f5ff; border-color: #adc6ff;",
  inner: "background: #adc6ff;",
  bg: "#f0f5ff"
}

const createStyledString = (obj) => {
  let {
    text,
    tags
  } = obj;
  tags.sort((a, b) => b.start - a.start);
  
  // TO KEEP TRACK OF INSERTED TEXT
  let insertedAmmount = []
  
  tags.forEach(t => {
    const {
      start,
      end,
      value,
      tag
    } = t;
    
    // COMPUTE THE REAL START AND END POSITIONS
    // TAKING INSERTED TEXT INTO ACCOUNT
    let realStart = start
    let realEnd = end
    for(let idx in insertedAmmount){
      if(idx < start){
        realStart += insertedAmmount[idx]
      }
      if(idx <= end){
        realEnd += insertedAmmount[idx]
      }
    }
    
    let pre = `<span contenteditable="false" data-action="${value}---${start}---${end}---${color.bg}---${tag}" unselectable="on" onselectstart="return false;" name="tag" class="outer" style="${color.outer}">`
    
    let pos = `<span class="ne-c-inner" unselectable="on" onselectstart="return false;" data-action="${value}---${start}---${end}---${color.bg}---${tag}" style="${color.inner}">${tag}</span></span>`
    
    // UPDATE THE INFORMATION ABOUT INSERTED TEXT
    insertedAmmount[start] = (insertedAmmount[start] || 0) + pre.length
    insertedAmmount[end] = (insertedAmmount[end] || 0) + pos.length
    
    // DON'T INSERT DIRECTLY THE VALUE BUT THE ALREADY EXISTENT TEXT
    text = text.substring(0, realStart) 
      + pre
      + text.substring(realStart, realEnd)
      + pos
      + text.substring(realEnd)
  });
  return text;
}
document.body.innerHTML=createStyledString(tagObject)