我有一段文字。
"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;
};
};
答案 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()
将字符串转换为包含字母的数组,并循环遍历结果数组。在循环中找到字母索引等于其start
或end
的对象。
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 )