JavaScript-将HTML标记插入给定样式索引的字符串中

时间:2018-12-07 08:37:25

标签: javascript html

我有一段文字。

"This is a test to see whether nested style spans work properly."

我将样式信息作为JSON对象,例如

0: {start: 22, end: 54, type: "strong"}
1: {start: 30, end: 36, type: "hyperlink", data: {…}}
2: {start: 37, end: 48, type: "em"}
3: {start: 43, end: 48, type: "hyperlink", data: {…}}

文字应该看起来像

<p>This is a test to see <strong>whether <a href="https://www.google.co.za">nested</a> <em>style <a href="https://www.google.co.za">spans</a></em> work </strong>properly.</p>

算法的一般方法应该是什么?问题在于,一旦我开始在文本中插入标记以设置样式,样式信息中包含的索引就会明显过时。

我试图跟踪通过缓冲区插入的字符的长度(以便可以根据缓冲区的长度来调整索引),但是这成为嵌套标签的问题。我的整个“解决方案”都显得笨拙和笨拙,我确信必须有一个更好的方法。

这是我尝试的代码。

NewsUtils.styleSpanCSS = span => {
let styledSpan = {};
  switch (span.type) {
    case "em":
      styledSpan.opening = "<span";
      styledSpan.style = ' class="italic">';
      styledSpan.closing = "</span>";
      break;
    case "hyperlink":
      styledSpan.opening = `<a href="${span.data.url}">`;
      styledSpan.style = "";
      styledSpan.closing = "</a>";
      break;
    case "strong":
      styledSpan.opening = "<span";
      styledSpan.style = ' class="bold">';
      styledSpan.closing = "</span>";
      break;
    default:
      styledSpan.opening = "";
      styledSpan.style = "";
      styledSpan.closing = "";
  }
  styledSpan.length =
    styledSpan.opening.length +
    styledSpan.style.length +
    styledSpan.closing.length;
  return styledSpan;
};

NewsUtils.styleParagraph = elem => {

  if (elem.spans.length > 0) {
    let buffer = 0;
    elem.spans.map(span => {
      let elementToInsert = NewsUtils.styleSpanCSS(span);
      let spanLength =
        elementToInsert.opening.length +
        elementToInsert.style.length +
        elementToInsert.closing.length;
      elem.text =
        elem.text.substring(0, span.start + buffer) +
        elementToInsert.opening +
        elementToInsert.style +
        elem.text.substring(span.start + buffer, span.end + buffer) +
        elementToInsert.closing +
        elem.text.substring(span.end + buffer, elem.text.length + buffer);
      buffer += spanLength;);
    });
    return <p dangerouslySetInnerHTML={{ __html: elem.text }} />;
  }
  return <p dangerouslySetInnerHTML={{ __html: elem.text }} />;
};

NewsUtils.markupParagraphs = post => {
  const postDetails = post.data.text.map(elem => {
  switch (elem.type) {
    case "paragraph":
      return NewsUtils.styleParagraph(elem);
    case "image":
      return (
        <img
          src={elem.url}
          width={elem.dimensions.width}
          height={elem.dimensions.height}
        />
      );
    case "embed":
      let url = elem.oembed.embed_url;
      url = url.substring(0, url.indexOf("&"));
      url = url.replace("watch?v=", "embed/");
      url = url.replace("vimeo.com", "player.vimeo.com/video");
      return <iframe src={url} frameBorder="0" allowFullScreen />;
    default:
      return null;
  }
  });
  return postDetails;
};
};

4 个答案:

答案 0 :(得分:3)

这是一个基本的实现。我摆脱了超链接的特殊处理以演示算法本身,但是将逻辑添加回去应该很容易:

const text = 'This is a test to see whether nested style spans work properly.'
const styling = [
  {start: 22, end: 54, type: "strong"},
  {start: 30, end: 36, type: "a"},
  {start: 37, end: 48, type: "em"},
  {start: 43, end: 48, type: "a"}
];

const result = [...text].reduce((a, v, i) => {
  styling.filter(s => s.start === i).forEach(s => a += `<${s.type}>`);
  styling.filter(s => s.end === i).forEach(s => a += `</${s.type}>`);
  return a + v;
}, '');

document.body.innerHTML = result;

输出:

This is a test to see <strong>whether <a>nested</a> <em>style <a>spans</em></a> work </strong>properly.

如果输入和样式数组很大,则可能需要创建临时查找对象以提高性能。

答案 1 :(得分:0)

您需要使用.split()将字符串转换为包含字母的数组,并循环遍历结果数组。在循环中找到字母索引等于其startend的对象。

var newStr = "";
// Loop through letters
str.split('').forEach(function(letter, i){
  // Loop through object
  for (key in obj){
    // If letter index is equal to key start
    if (obj[key].start == i)
      newStr += obj[key].type == "hyperlink" ? 
        '<a href="https://www.google.co.za">' : 
        '<'+obj[key].type+'>';
    // If letter index is equal to key end
    if (obj[key].end == i)
      newStr += obj[key].type == "hyperlink" ? 
        '</a>' : 
        '</'+obj[key].type+'>';
  }
  newStr += letter;
});
// Wrap result in <p></p>
newStr = "<p>"+newStr+"</p>";
document.write(newStr);

var str = "This is a test to see whether nested style spans work properly.";
var obj = {
  0: {start: 22, end: 54, type: "strong"},
  1: {start: 30, end: 36, type: "hyperlink", data: {}},
  2: {start: 37, end: 48, type: "em"},
  3: {start: 43, end: 48, type: "hyperlink", data: {}}
};

var newStr = "";
str.split('').forEach(function(letter, i){
  for (key in obj){
    if (obj[key].start == i)
      newStr += obj[key].type == "hyperlink" ? 
        '<a href="https://www.google.co.za">' : 
        '<'+obj[key].type+'>';
    if (obj[key].end == i)
      newStr += obj[key].type == "hyperlink" ? 
        '</a>' : 
        '</'+obj[key].type+'>';
  }
  newStr += letter;
});
newStr = "<p>"+newStr+"</p>";

答案 2 :(得分:0)

这种方法首先将给定位置排序到最内部范围,然后采用外部样式。

var string = 'This is a test to see whether nested style spans work properly.',
    makeup = [{ start: 0, end: 63, type: "p" }, { start: 22, end: 54, type: "strong" }, { start: 30, end: 36, type: "hyperlink", data: { link: 'http://example.com/#1' } }, { start: 37, end: 48, type: "em" }, { start: 43, end: 48, type: "hyperlink", data: { link: 'http://example.com/#2' } }],
    sorted = [],
    positions = Array.from(string),
    i, j,
    result;

while (makeup.length > 1) {
    i = 0;
    j = 1;
    while (j < makeup.length) {
        if (makeup[j].start >= makeup[i].start && makeup[j].end <= makeup[i].end) {
            i = j;
        }
        j++;
    }
    sorted.push(makeup.splice(i, 1)[0]);
}
sorted.push(makeup.splice(0, 1)[0]);

sorted.forEach(({ start, end, type, data }) => {
    var [header, footer] = type === 'hyperlink'
        ? [`<a href="${ data.link }">`, '</a>']
        : [`<${ type }>`, `</${ type }>`];

    positions[start] = header + positions[start];
    positions[end - 1] += footer;
});

result = positions.join('');
console.log(result);
document.body.innerHTML += result;

答案 3 :(得分:0)

拆分为字符数组可以在特定索引处插入而不影响其余索引:

const text = 'This is a test to see whether nested style spans work properly.'
const json = `{ "0": {"start": 22, "end": 54, "type": "strong"},
                "1": {"start": 30, "end": 36, "type": "hyperlink"},
                "2": {"start": 37, "end": 48, "type": "em"},
                "3": {"start": 43, "end": 48, "type": "hyperlink"} }`

const letters = text.split('')
JSON.parse(json, (k, v) => v.type ? 
  (letters[v.start] = '<' + v.type + '>' + letters[v.start], 
   letters[v.end]  = '</' + v.type + '>' + letters[v.end]   ) : v)

console.log( letters.join('') )
console.log( letters )