web-dev-qa-db-ja.com

ReactJSを使用してテキストを強調表示する

クエリに一致するテキストを強調表示しようとしていますが、タグをテキストではなく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

19
Andrew Hunt

これが私のシンプルな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>;
}
38
peter.bartos

必要なことを実行するための NPM上のReactコンポーネント がすでにあります。

var Highlight = require('react-highlighter');
[...]
<Highlight search={regex}>{name}</Highlight>
5
aij

以下は、標準の<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"/>
4
Henok T

デフォルトでは、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>
        );
    }
3
Yanik Ceulemans

これが私の解決策です。

シンプルさとパフォーマンスに焦点を当てようとしたので、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 を確認してください。

0
Yoav Kadosh

別のアプローチを使用することをお勧めします。 _<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()メソッドがトリガーされます。各子は新しいクエリを新しい小道具として受け取り、テキストを出力して、クエリに一致する部分を強調表示します。

0
gcedo
  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>
0