Search
Provides full-text search for the rendered Markdown.
Basic Usage
Use target to specify the element to search. It supports CSS selector, DOM element, component instance, or a function that returns any of these. You can also bind the keyword via v-model. When the keyword matches the content, it automatically scrolls to the match and highlights it.
View Source
<template>
<div>
<vmd-search v-model="keyword" :target="renderRef" />
<vmd-render ref="renderRef" style="height: 436px; overflow: auto" :src="md" />
</div>
</template>
<script setup>
import { ref } from 'vue'
const keyword = ref('or')
const renderRef = ref(null)
const md = `
# h1 Heading 8-)
## h2 Heading
### h3 Heading
#### h4 Heading
##### h5 Heading
###### h6 Heading
## Horizontal Rules
___
---
***
## Emphasis
**This is bold text**
__This is bold text__
*This is italic text*
_This is italic text_
~~Strikethrough~~
## Blockquotes
> Blockquotes can also be nested...
>> ...by using additional greater-than signs right next to each other...
> > > ...or with spaces between arrows.
## Lists
Unordered
+ Create a list by starting a line with \`+\`, \`-\`, or \`*\`
+ Sub-lists are made by indenting 2 spaces:
- Marker character change forces new list start:
* Ac tristique libero volutpat at
+ Facilisis in pretium nisl aliquet
- Nulla volutpat aliquam velit
+ Very easy!
Ordered
1. Lorem ipsum dolor sit amet
2. Consectetur adipiscing elit
3. Integer molestie lorem at massa
1. You can use sequential numbers...
1. ...or keep all the numbers as \`1.\`
Start numbering with offset:
57. foo
1. bar
## Tables
| Option | Description |
| ------ | ----------- |
| data | path to data files to supply the data that will be passed into templates. |
| engine | engine to be used for processing templates. Handlebars is the default. |
| ext | extension to be used for dest files. |
Right aligned columns
| Option | Description |
| ------:| -----------:|
| data | path to data files to supply the data that will be passed into templates. |
| engine | engine to be used for processing templates. Handlebars is the default. |
| ext | extension to be used for dest files. |
`
</script>Clearable
Use clearable to show a clear button in the search. Click it to clear the keyword.
View Source
<template>
<div>
<p>
<label>
<input v-model="clearable" name="checkbox" type="checkbox" />
Enable clearable
</label>
</p>
<vmd-search v-model="keyword" :clearable="clearable" />
</div>
</template>
<script setup>
import { ref } from 'vue'
const keyword = ref('vue-markdown-design')
const clearable = ref(true)
</script>Close Button
Use close-icon to show a close button. Clicking it triggers the close event.
View Source
<template>
<div>
<p>
<label>
<input v-model="closeIcon" name="checkbox" type="checkbox" />
Enable close-icon
</label>
</p>
<vmd-search :close-icon="closeIcon" @close="onClose" />
</div>
</template>
<script setup>
import { ref } from 'vue'
const closeIcon = ref(true)
const onClose = () => {
console.log('Clicked the close button')
}
</script>Border
Use border to show the bottom border of the search input.
View Source
<template>
<div>
<p>
<label>
<input v-model="border" name="checkbox" type="checkbox" />
Enable border
</label>
</p>
<vmd-search :border="border" />
</div>
</template>
<script setup>
import { ref } from 'vue'
const border = ref(true)
</script>Size
Use size to set the search size. It supports huge, large, normal, and small.
View Source
<template>
<div>
<p>
<label>
Select search size:
<select v-model="size" name="select">
<option value="huge">huge (default)</option>
<option value="large">large</option>
<option value="normal">normal</option>
<option value="small">small</option>
</select>
</label>
</p>
<vmd-search placeholder="Type to search" :size="size" />
</div>
</template>
<script setup>
import { ref } from 'vue'
const size = ref('huge')
</script>Disabled
Use disabled to disable the search. When disabled, searching and related actions are unavailable.
View Source
<template>
<div>
<p>
<label>
<input v-model="disabled" name="checkbox" type="checkbox" />
Enable disabled
</label>
</p>
<vmd-search v-model="keyword" :target="renderRef" :disabled="disabled" />
<vmd-render ref="renderRef" :src="md" />
</div>
</template>
<script setup>
import { ref } from 'vue'
const disabled = ref(true)
const keyword = ref('or')
const renderRef = ref(null)
const md = `
# Lorem ipsum
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi maximus elit fermentum pellentesque vehicula. Suspendisse potenti. Donec iaculis consectetur erat nec placerat. Suspendisse facilisis justo sit amet hendrerit sollicitudin. Suspendisse commodo malesuada massa, ac elementum risus. Ut eu facilisis neque. Fusce tincidunt, ligula vitae eleifend venenatis, purus purus ultrices purus, nec maximus tellus lectus nec leo. Sed auctor magna sed quam dapibus dapibus. Nullam ornare ultricies sem, a iaculis sapien volutpat euismod. Sed ac dictum nulla. Duis euismod tellus vitae diam hendrerit, sit amet vestibulum mauris rhoncus.
`
</script>Placeholder
Use placeholder to customize the placeholder text of the search input.
View Source
<template>
<vmd-search placeholder="🔍 Type a keyword to search" />
</template>Scroll Offset
When switching between matches triggers scrolling, you can set offset to adjust the scroll position. It supports numbers and string positions (center, start, end, nearest). When the component is nested inside multiple scroll containers, the scroll behavior and offset only apply to the nearest scroll container.
TIP
The scrolling mode is if-needed. In short, scrolling only happens when the next match is outside the visible area.
View Source
<template>
<div>
<h4>Number:</h4>
<div style="margin-bottom: 16px">
<vmd-search v-model="keyword" :offset="56" :target="renderWhenNumber" />
<div style="flex: auto; position: relative">
<div
style="
width: 100%;
background: #0969da;
position: absolute;
top: 0;
color: #fff;
padding: 16px 32px;
box-sizing: border-box;
z-index: 1;
"
>
Fixed header
</div>
<div style="height: 436px; overflow: auto">
<vmd-render
ref="renderWhenNumber"
style="margin-top: 56px; height: 436px; overflow: auto"
:src="md"
/>
</div>
</div>
</div>
<h4>String:</h4>
<div>
<vmd-search v-model="keyword" offset="end" :target="renderWhenPosition" />
<div style="flex: auto; position: relative">
<div
style="
width: 100%;
background: #0969da;
position: absolute;
top: 0;
color: #fff;
padding: 16px 32px;
box-sizing: border-box;
z-index: 1;
"
>
Fixed header
</div>
<div style="height: 436px; overflow: auto">
<vmd-render ref="renderWhenPosition" style="margin-top: 56px" :src="md" />
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const keyword = ref('or')
const renderWhenNumber = ref(null)
const renderWhenPosition = ref(null)
const md = `
# h1 Heading 8-)
## h2 Heading
### h3 Heading
#### h4 Heading
##### h5 Heading
###### h6 Heading
## Horizontal Rules
___
---
***
## Emphasis
**This is bold text**
__This is bold text__
*This is italic text*
_This is italic text_
~~Strikethrough~~
## Blockquotes
> Blockquotes can also be nested...
>> ...by using additional greater-than signs right next to each other...
> > > ...or with spaces between arrows.
## Lists
Unordered
+ Create a list by starting a line with \`+\`, \`-\`, or \`*\`
+ Sub-lists are made by indenting 2 spaces:
- Marker character change forces new list start:
* Ac tristique libero volutpat at
+ Facilisis in pretium nisl aliquet
- Nulla volutpat aliquam velit
+ Very easy!
Ordered
1. Lorem ipsum dolor sit amet
2. Consectetur adipiscing elit
3. Integer molestie lorem at massa
1. You can use sequential numbers...
1. ...or keep all the numbers as \`1.\`
Start numbering with offset:
57. foo
1. bar
## Tables
| Option | Description |
| ------ | ----------- |
| data | path to data files to supply the data that will be passed into templates. |
| engine | engine to be used for processing templates. Handlebars is the default. |
| ext | extension to be used for dest files. |
Right aligned columns
| Option | Description |
| ------:| -----------:|
| data | path to data files to supply the data that will be passed into templates. |
| engine | engine to be used for processing templates. Handlebars is the default. |
| ext | extension to be used for dest files. |
`
</script>Smooth Scrolling
Use smooth to enable smooth scrolling when navigating to matches.
View Source
<template>
<div>
<p>
<label>
<input v-model="smooth" name="checkbox" type="checkbox" />
Enable smooth
</label>
</p>
<div>
<vmd-search v-model="keyword" :target="renderRef" :smooth="smooth" />
<vmd-render ref="renderRef" style="height: 436px; overflow: auto" :src="md" />
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const renderRef = ref(null)
const keyword = ref('or')
const smooth = ref(true)
const md = `
# h1 Heading 8-)
## h2 Heading
### h3 Heading
#### h4 Heading
##### h5 Heading
###### h6 Heading
## Horizontal Rules
___
---
***
## Emphasis
**This is bold text**
__This is bold text__
*This is italic text*
_This is italic text_
~~Strikethrough~~
## Blockquotes
> Blockquotes can also be nested...
>> ...by using additional greater-than signs right next to each other...
> > > ...or with spaces between arrows.
## Lists
Unordered
+ Create a list by starting a line with \`+\`, \`-\`, or \`*\`
+ Sub-lists are made by indenting 2 spaces:
- Marker character change forces new list start:
* Ac tristique libero volutpat at
+ Facilisis in pretium nisl aliquet
- Nulla volutpat aliquam velit
+ Very easy!
Ordered
1. Lorem ipsum dolor sit amet
2. Consectetur adipiscing elit
3. Integer molestie lorem at massa
1. You can use sequential numbers...
1. ...or keep all the numbers as \`1.\`
Start numbering with offset:
57. foo
1. bar
## Tables
| Option | Description |
| ------ | ----------- |
| data | path to data files to supply the data that will be passed into templates. |
| engine | engine to be used for processing templates. Handlebars is the default. |
| ext | extension to be used for dest files. |
Right aligned columns
| Option | Description |
| ------:| -----------:|
| data | path to data files to supply the data that will be passed into templates. |
| engine | engine to be used for processing templates. Handlebars is the default. |
| ext | extension to be used for dest files. |
`
</script>Input Attributes
Use input-attrs to pass attributes to the internal input element.
View Source
<template>
<vmd-search v-model="keyword" :input-attrs="{ maxlength: 19 }" />
</template>
<script setup>
import { ref } from 'vue'
const keyword = ref('Up to 19 characters')
</script>Toggle Current Match
Use the toggle method to manually switch the current match index. It accepts two parameters: the first is the target index (a number or a string: prev / next). It automatically clamps out-of-range values and returns a safe index via index-change. The second parameter controls whether to ignore the disabled state. It defaults to true, meaning toggle can still switch the index even when the search is disabled.
View Source
<template>
<div>
<p>
<label style="margin-right: 10px">
Enter index:
<input v-model="index" name="number" type="number" style="width: 4em" />
</label>
<button @click="searchRef.toggle(index)">Go</button>
</p>
<p>Current index (starting at 0): {{ safeIndex }}</p>
<vmd-search
v-model="keyword"
ref="searchRef"
:target="renderRef"
@index-change="safeIndex = $event"
/>
<vmd-render ref="renderRef" style="height: 436px; overflow: auto" :src="md" />
</div>
</template>
<script setup>
import { ref } from 'vue'
const renderRef = ref(null)
const searchRef = ref(null)
const keyword = ref('or')
const index = ref(0)
const safeIndex = ref(0)
const md = `
# h1 Heading 8-)
## h2 Heading
### h3 Heading
#### h4 Heading
##### h5 Heading
###### h6 Heading
## Horizontal Rules
___
---
***
## Emphasis
**This is bold text**
__This is bold text__
*This is italic text*
_This is italic text_
~~Strikethrough~~
## Blockquotes
> Blockquotes can also be nested...
>> ...by using additional greater-than signs right next to each other...
> > > ...or with spaces between arrows.
## Lists
Unordered
+ Create a list by starting a line with \`+\`, \`-\`, or \`*\`
+ Sub-lists are made by indenting 2 spaces:
- Marker character change forces new list start:
* Ac tristique libero volutpat at
+ Facilisis in pretium nisl aliquet
- Nulla volutpat aliquam velit
+ Very easy!
Ordered
1. Lorem ipsum dolor sit amet
2. Consectetur adipiscing elit
3. Integer molestie lorem at massa
1. You can use sequential numbers...
1. ...or keep all the numbers as \`1.\`
Start numbering with offset:
57. foo
1. bar
## Tables
| Option | Description |
| ------ | ----------- |
| data | path to data files to supply the data that will be passed into templates. |
| engine | engine to be used for processing templates. Handlebars is the default. |
| ext | extension to be used for dest files. |
Right aligned columns
| Option | Description |
| ------:| -----------:|
| data | path to data files to supply the data that will be passed into templates. |
| engine | engine to be used for processing templates. Handlebars is the default. |
| ext | extension to be used for dest files. |
`
</script>Manual Refresh
Use the refresh method to refresh search results manually. It accepts a boolean parameter that defaults to true, indicating that the current index will be reset while refreshing results.
TIP
If you use a Render component instance as the search target, manual refresh is usually unnecessary because Search automatically refreshes when the content changes.
View Source
<template>
<div>
<p>
<label style="margin-right: 10px">
Select content:
<select v-model="index" name="select">
<option v-for="(item, index) in options" :key="item.title" :value="index">
{{ item.title }}
</option>
</select>
</label>
<button @click="refresh">Update</button>
</p>
<vmd-search v-model="keyword" ref="searchRef" target="#refreshRef" />
<vmd-render id="refreshRef" :src="md" />
</div>
</template>
<script setup>
import { ref } from 'vue'
const options = [
{
title: 'Lorem',
value:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi maximus elit fermentum pellentesque vehicula. Suspendisse potenti. Donec iaculis consectetur erat nec placerat. Suspendisse facilisis justo sit amet hendrerit sollicitudin. Suspendisse commodo malesuada massa, ac elementum risus. Ut eu facilisis neque. Fusce tincidunt, ligula vitae eleifend venenatis, purus purus ultrices purus, nec maximus tellus lectus nec leo. Sed auctor magna sed quam dapibus dapibus. Nullam ornare ultricies sem, a iaculis sapien volutpat euismod. Sed ac dictum nulla. Duis euismod tellus vitae diam hendrerit, sit amet vestibulum mauris rhoncus.'
},
{
title: 'Sed',
value:
'Sed lectus nisl, blandit at volutpat et, lobortis ac urna. Etiam elementum id mauris a ultricies. Suspendisse rhoncus est justo, eu pellentesque turpis elementum quis. Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque vel dolor orci. Vestibulum rhoncus erat vitae molestie porttitor. Phasellus accumsan risus ut enim mollis, quis feugiat massa auctor. Donec vehicula convallis nisi sit amet dapibus. Integer felis erat, interdum eget erat non, facilisis vulputate augue. Ut sed tortor vitae ante venenatis rhoncus eget id tortor. Etiam molestie luctus ligula sit amet venenatis. Aliquam iaculis tristique sem, vitae convallis mi ultricies nec.'
}
]
const keyword = ref('or')
const index = ref(0)
const md = ref(options[0].value)
const searchRef = ref(null)
const refresh = () => {
md.value = options[index.value].value
searchRef.value?.refresh()
}
</script>API
Props
| Name | Description | Type | Default |
|---|---|---|---|
| v-model | Search keyword | string / number | '' |
| target | Search target element | string / HTMLElement / VueInstance / ()=> string | HTMLElement | VueInstance | - |
| clearable | Whether to show the clear button in the search | boolean | true |
| close-icon | Whether to show the close button in the search | boolean | true |
| border | Whether to show the bottom border of the search | boolean | true |
| size | Search input size | 'huge' | 'large' | 'normal' | 'small' | huge |
| disabled | Whether to disable the search | boolean | false |
| placeholder | Search input placeholder text | string | - |
| offset | Scroll offset when switching the search match index | number / 'start'|'end'|'center'|'nearest' | center |
| smooth | Whether to enable smooth scrolling when navigating between search matches | boolean | false |
| input-attrs | Attributes passed to the internal search input element | object | {} |
Events
| Name | Description | Arguments |
|---|---|---|
| input | Triggered when the search keyword changes | event: Event |
| blur | Triggered when the search input loses focus | event: FocusEvent |
| focus | Triggered when the search input gains focus | event: FocusEvent |
| change | Triggered only when the search input blurs or Enter is pressed | event: Event |
| clear | Triggered when clicking the search clear button | - |
| close | Triggered when clicking the search close button | - |
| step-click | Triggered when clicking the search previous/next buttons | value: 'prev' | 'next' |
| total-change | Triggered when the total search match count changes | value: number |
| index-change | Triggered when the current search match index changes | value: number |
Methods
| Name | Description | Type |
|---|---|---|
| focus | Focus the search input | () => void |
| blur | Blur the search input | () => void |
| clear | Clear the search keyword | () => void |
| toggle | Switch the current search match index | (index: number | 'prev' | 'next', ignoreDisabled?: boolean) => void |
| refresh | Refresh search results manually | (resetIndex?: boolean) => void |