How to remove HTML tags from rendered text

Viewed 27197

I am needing to remove the opening and closing <p> tags from some rendered comment text. I pass the content to a component as a prop and i think that in doing so, it doesn't allow for the v-html directive to work correctly.

I need the content to render without the html tags

Here is where I am trying to render normally with v-html

 <textarea class="form-control comment-inline-edit" v-html="content" name="comment-inline-edit" cols="45" rows="3"></textarea>

And here is where I am passing the rendered content from the parent component

<CommentEdit v-show="isEditting" :content="comment.content.rendered" v-on:cancel="cancelEdit" />

Is there a VueJS way to do this other than using v-html?

4 Answers

I would recommend you strip HTML from rendered text in VueJS using a filter, that way you can repeatedly use the filter around the application, rather than a specific singular computation.

I wrote the following, which utilizes the browser's parsing (most reliable method) as regex can be thwarted by user silliness:

Vue.filter('stripHTML', function (value) {
  const div = document.createElement('div')
  div.innerHTML = value
  const text = div.textContent || div.innerText || ''
  return text
});

Once you include this in your app.js you can then render it anywhere as follows:

{{ pdf.description | stripHTML }}
  <p> @{{data.description | strippedContent}} </p>


filters: {
    strippedContent: function(string) {
           return string.replace(/<\/?[^>]+>/ig, " "); 
    }
}

Working for me

I was alarmed by @mikep's comment to @Grant's answer about XSS vulnerability. This would be a show stopper for me.

A quick test confirms it to be true:

        {{foo| stripHTML}}
...

  data:function(){return {
    foo: "<p><img src='' onerror=alert('XSS')/>some text</p>",
  }},

This shows the alert, proving the Javascript was injected and executed.

I found a technique to parse the HTML without attaching it to the DOM in this answer which is not vulnerable to the image attack.

I came up with this version of Grant's filter which simply renders the same test to "some text" with no alert shown.

Vue.filter('stripHTML', function (value) {
    let text = ''
    const parser = new DOMParser();
    const dom = parser.parseFromString(value, "text/html");
    let div = dom.querySelector('body>*');
    if(div){
        text = div.textContent || div.innerText || ''
    }
    return text
});

Note this assumes there's a single top-level node in the content, like <p>some stuff<p>, but you could extend the query selector to handle other cases.

Related