I have user-generated data I'm displaying in a Vue app, so the default Vue behavior of html-escaping the data is perfect. Except, now I'd like users to be able to search that data, and I'd like to highlight the matching text in the search result. That means I need my own styling to not be escaped, even though all the original data should still be escaped.
In other words I need to apply my styling after the data has been html-escaped, eg:
1. user inputs data:
some original data that has special characters like > and <
2. Vue html-escapes this for safe display:
some original data that has special characters like > and <
3. dynamically style the search results
Eg if user searched for "original data" it becomes:
some <span >original data</span> that has special characters like > and <
Notice how my dynamic styling was not html escaped even though the user input was.
I could of course just use v-html to bypass the html escape entirely, but then I lose all the safety and benefit of html escaping which I don't want to lose. Ideally I want to explicitly call Vue's html escape routine, then apply my styling so that it does not get escaped, then finally render all of that unescaped (since I already applied appropriate escaping programmatically).
Does Vue offer programmatic access to its html escape routine? (And I'm not talking about $sanitize which strips out special characters entirely, I want to preserve them just like normal Vue templating does). I could of course write my own escape routine, just wondered if I could leverage Vue's instead.
CodePudding user response:
Vue uses the Browser's API for encoding HTML content, as mentioned here: https://vuejs.org/v2/guide/security.html#HTML-content.
So, something like this should offer you the same kind of protection as Vue would from the raw user input. In the computed property, we pass the user data through the p element to encode it. Then we chain on top of that our own highlight computed property where we can inject our own HTML, and then show that with v-html.
<template>
<div id="app">
<div><label>Raw text:<br /><textarea v-model="text" cols="50" rows="10" /></label></div>
<div><label>Search for: <input type="text" v-model="search" /></label></div>
<p><label>v-html: <span v-html="text" /></label></p>
<p><label>Highlighted: <span v-html="highlight" /></label></p>
</div>
</template>
<script>
export default {
data() {
return {
text: "some original data that has special characters like > and <",
search: "original data"
}
},
computed: {
highlight() {
const html = this.safeHtml;
return html.replace(this.search, "<span class='my-highlight-style'>$&</span>");
},
safeHtml() {
var p = document.createElement("p");
p.textContent = this.text;
return p.innerHTML;
}
}
}
</script>
<style>
.my-highlight-style {
background: orange;
padding: 5px;
}
</style>
