16
7

More than 3 years have passed since last update.

【Vue】filtersで検索キーワードをハイライト!

Last updated at Posted at 2019-07-03

Vue.jsでこの「検索キーワードのハイライト」がしたかったスクリーンショット 2019-07-04 1.20.20.png

Vue.jsのfiltersを使えばカンタンだね

1. まず、用意

vue-cliで、プロジェクト作るまでは割愛するよ〜

プロジェクトが出来たら、App.vue, components/Media.vueに、サンプルテキストを準備する

/App.vue
<template>
  <div id="app">
    <div class="container">
      <div class="col">
        <div class="row">
          <input class="form-control col-6"
                 type="text"
                 v-model="search" // 検索キーワード
                 placeholder="検索"
                 aria-label="Search">
        </div>
        <media v-for="(media, index) in medias"
               :key="index"
               :heading="media.heading"
               :body="media.body"
               class="mt-3"/>
      </div>
    </div>
  </div>
</template>

<script>
import Media from './components/Media'
export default {
  name: 'app',
  components: {
    Media
  },
  data () {
    return {
      search: '', // 検索キーワード
      medias: [
        {
          heading: 'その1',
          body: 'それも直接あにその始末心というののところをしたた。\n'
        },
        {
          heading: 'その2',
          body: 'ひょろひょろ始めに建設人はたといこの発展たですまでを行かて行かますをは盲従出さでだから、もともとにはできないたたた。'
        },
        {
          heading: 'その3',
          body: '個人をすむたい事もむしろ偶然に同じくたただ。\n'
        },
        {
          heading: 'その4',
          body: 'やはり槙さんへ想像がたどう安心に繰り返しです嚢この人皆か関係にってご返事ですますなかっですから、そうした今は私か国家学校があって、木下さんのものの学生のあなたにいよいよご演説と勧めてあなた主義で大発展に移れように何しろご講演と得んないて、はなはだもっとも発展が構わないて来るん方にかけるなりない。それならもっともご権力を聞いものはぴたり正直と直さですて、どんな頭にはすれないばといった中からしが来なない。'
        },
        {
          heading: 'その5',
          body: 'その時国の時同じ釣はあなた中をありでしょかと岡田さんに云ったう、新のたくさんですという小立脚なだませので、自己のところが他がほかかもの仲間を今込み入って行かで、少々の今をしのでその時にああ使いこなすでたといだつもりまして、忌まわしいらしいたて少しお作物知らで事んなた。'
        }
      ]
    }
  }
}
</script>
/components/Media.vue
<template>
    <div class="media">
        <img class="mr-3" src="https://via.placeholder.com/150" alt="Generic placeholder image">
        <div class="media-body">
            <h5 class="mt-0">{{ heading }}</h5>
            <p>{{ body }}</p>
        </div>
    </div>
</template>

<script>
export default {
    props: ['heading', 'body']
}
</script>

見た目
スクリーンショット 2019-07-04 1.38.23.png

2. filtersを追加

公式のフィルターの項目を参考に、searchHighlightを追加してみよう

App.vue
<template>
  <div id="app">
    <div class="container">
      <div class="col">
        <div class="row">
          <input v-model="search" class="form-control col-6" type="text" placeholder="検索" aria-label="Search">
        </div>
        <media v-for="(media, index) in medias"
               :key="index"
               :heading="media.heading"
               :body="media.body"
               :search="search" // ここを追加 フィルターに使う検索キーワードを渡す
               class="mt-3"/>
      </div>
    </div>
  </div>
</template>
/components/Media.vue
<template>
    <div class="media">
        <img class="mr-3" src="https://via.placeholder.com/150" alt="Generic placeholder image">
        <div class="media-body">
            <!-- | を使って、フィルターした値を表示できるぜ -->
            <h5 class="mt-0">{{ heading | searchHighlight(search) }}</h5>
            <p>{{ body | searchHighlight(search) }}</p>
        </div>
    </div>
</template>

<script>
export default {
    props: ['heading', 'body', 'search'], // propsにsearchを追加
    filters: {
        searchHighlight (value, search) {
            // 検索キーワードが入力されているとき
            if (search) {
                // 検索キーワードに一致する部分を、spanタグに置換すればいいね
                return value.replace(search, `<span class="bg-warning">${search}</span>`)
            }
            // 検索キーワードが入力されていない場合は、ハイライトしない
            return value
        }
    }
}
</script>

すると、、、
スクリーンショット 2019-07-04 1.49.59.png

おや?エスケープされているぞ。

3. v-htmlを使いなさい

公式の見解によると、
スクリーンショット 2019-07-04 1.53.22.png
v-htmlディレクティブを使えとさ。

v-htmlで、フィルターを使うには、Google先生に聞いたところ


<h5 class="mt-0" v-html="$options.filters.searchHighlight(heading, search)"></h5>

こう書くらしい。(optionsの中身を知りたい方は、mounted()メソッド内で、console.log(this.$options)をしてみよう)

この書き方をすると、

/components/Media.vue
<template>
    <div class="media">
        <img class="mr-3" src="https://via.placeholder.com/150" alt="Generic placeholder image">
        <div class="media-body">
            <!-- v-htmlを使用 -->
            <h5 class="mt-0" v-html="$options.filters.searchHighlight(heading, search)"></h5>
            <p v-html="$options.filters.searchHighlight(body, search)"></p>
        </div>
    </div>
</template>

<script>
export default {
    props: ['heading', 'body', 'search'],
    filters: {
        searchHighlight (value, search) {
            if (search) {
                return value.replace(search, `<span class="bg-warning">${search}</span>`)
            }
            return value
        }
    }
}
</script>

すると、、、
スクリーンショット 2019-07-04 2.06.33.png

出来、、、、てない!

各文の最初の「た」 しか、ハイライトされていない!!

でも正規表現を使えば解決だ

4. 正規表現を使う

        searchHighlight (value, search) {
            if (search) {
                // 正規表現で、一致する文字全てをハイライトする
                const searchRegExp = new RegExp(search, 'ig')
                return value.replace(searchRegExp, match => `<span class="bg-warning">${match}</span>`)
            }
            return value
        }

すると、
スクリーンショット 2019-07-04 2.10.53.png

出来た!!

ソースコードでございます

16
7
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
7