クエリに一致するテキストを強調表示しようとしていますが、タグをテキストではなくHTMLとして表示する方法がわかりません。
var Component = React.createClass({
_highlightQuery: function(name, query) {
var regex = new RegExp("(" + query + ")", "gi");
return name.replace(regex, "<strong>$1</strong>");
},
render: function() {
var name = "Javascript";
var query = "Java"
return (
<div>
<input type="checkbox" /> {this._highlightQuery(name, query)}
</div>
);
}
});
現在の出力:<strong> Java </ strong>スクリプト
望ましい出力:Java script
これが私のシンプルな2ライナーヘルパーメソッドです。
getHighlightedText(text, higlight) {
// Split text on higlight term, include term itself into parts, ignore case
var parts = text.split(new RegExp(`(${higlight})`, 'gi'));
return <span>{parts.map(part => part.toLowerCase() === higlight.toLowerCase() ? <b>{part}</b> : part)}</span>;
}
要求された部分が<b> </b>
タグで強調表示されているスパンを返します。これは、必要に応じて他のタグを使用するように簡単に変更できます。
PDATE:一意のキーが見つからないという警告を回避するために、スパンに基づいて、一致するパーツのfontWeightスタイルを設定するソリューションを次に示します。
getHighlightedText(text, higlight) {
// Split on higlight term and include term into parts, ignore case
let parts = text.split(new RegExp(`(${higlight})`, 'gi'));
return <span> { parts.map((part, i) =>
<span key={i} style={part.toLowerCase() === higlight.toLowerCase() ? { fontWeight: 'bold' } : {} }>
{ part }
</span>)
} </span>;
}
必要なことを実行するための NPM上のReactコンポーネント がすでにあります。
var Highlight = require('react-highlighter');
[...]
<Highlight search={regex}>{name}</Highlight>
以下は、標準の<mark>
タグを使用してテキストを強調表示するreactコンポーネントの例です。
const Highlighted = ({text = '', highlight = ''}) => {
if (!highlight.trim()) {
return <span>{text}</span>
}
const regex = new RegExp(`(${_.escapeRegExp(highlight)})`, 'gi')
const parts = text.split(regex)
return (
<span>
{parts.filter(part => part).map((part, i) => (
regex.test(part) ? <mark key={i}>{part}</mark> : <span key={i}>{part}</span>
))}
</span>
)
}
そして、ここでそれを使用する方法です
<Highlighted text="the quick brown fox jumps over the lazy dog" highlight="fox"/>
デフォルトでは、ReactJSはHTMLをエスケープしてXSSを防止します。 HTMLを設定したい場合は、特別な属性dangerouslySetInnerHTML
を使用する必要があります。次のコードを試してください:
render: function() {
var name = "Javascript";
var query = "Java"
return (
<div>
<input type="checkbox" /> <span dangerouslySetInnerHTML={{__html: this._highlightQuery(name, query)}}></span>
</div>
);
}
これが私の解決策です。
シンプルさとパフォーマンスに焦点を当てようとしたので、Reactの外部でDOMを手動で操作するソリューションや、dangerouslySetInnerHTML
のような安全でないメソッドは避けました。
さらに、このソリューションは、後続の一致を単一の<span/>
に結合することで、冗長なスパンの発生を回避します。
const Highlighter = ({children, highlight}) => {
if (!highlight) return children;
const regexp = new RegExp(highlight, 'g');
const matches = children.match(regexp);
console.log(matches, parts);
var parts = children.split(new RegExp(`${highlight.replace()}`, 'g'));
for (var i = 0; i < parts.length; i++) {
if (i !== parts.length - 1) {
let match = matches[i];
// While the next part is an empty string, merge the corresponding match with the current
// match into a single <span/> to avoid consequent spans with nothing between them.
while(parts[i + 1] === '') {
match += matches[++i];
}
parts[i] = (
<React.Fragment key={i}>
{parts[i]}<span className="highlighted">{match}</span>
</React.Fragment>
);
}
}
return <div className="highlighter">{parts}</div>;
};
使用法:
<Highlighter highlight='text'>Some text to be highlighted</Highlighter>
実際の例については、この codepen を確認してください。
別のアプローチを使用することをお勧めします。 _<TextContainer />
_要素を含む_<Text />
_というコンポーネントを1つ作成します。
_var React = require('react');
var Text = require('Text.jsx');
var TextContainer = React.createClass({
getInitialState: function() {
return {
query: ''
};
},
render: function() {
var names = this.props.names.map(function (name) {
return <Text name={name} query={this.state.query} />
});
return (
<div>
{names}
</div>
);
}
});
module.exports = TextContainer;
_
ご覧のとおり、テキストコンテナーは現在のクエリを状態として保持しています。ここで、_<Text />
_コンポーネントは次のようになります。
_var React = require('react');
var Text = React.createClass({
propTypes: {
name: React.PropTypes.string.isRequired,
query: React.PropTypes.string.isRequired
},
render: function() {
var query = this.props.query;
var regex = new RegExp("(" + query + ")", "gi");
var name = this.props.name;
var parts = name.split(regex);
var result = name;
if (parts) {
if (parts.length === 2) {
result =
<span>{parts[0]}<strong>{query}</strong>{parts[1]}</span>;
} else {
if (name.search(regex) === 0) {
result = <span><strong>{query}</strong>{parts[0]}</span>
} else {
result = <span>{query}<strong>{parts[0]}</strong></span>
}
}
}
return <span>{result}</span>;
}
});
module.exports = Text;
_
したがって、ルートコンポーネントには、現在のクエリの状態があります。状態が変更されると、子のrender()
メソッドがトリガーされます。各子は新しいクエリを新しい小道具として受け取り、テキストを出力して、クエリに一致する部分を強調表示します。
const escapeRegExp = (str = '') => (
str.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1')
);
const Highlight = ({ search = '', children = '' }) => {
const patt = new RegExp(`(${escapeRegExp(search)})`, 'i');
const parts = String(children).split(patt);
if (search) {
return parts.map((part, index) => (
patt.test(part) ? <mark key={index}>{part}</mark> : part
));
} else {
return children;
}
};
<Highlight search="la">La La Land</Highlight>