Prism: PrismJS doesn't highlight with VueJS

Created on 5 Jul 2017  路  10Comments  路  Source: PrismJS/prism

Hi, prism does not work when the content is loading async. (As with VueJS)

<pre id="editor">
  <code class="language-css">{{ paste.content }}</code>
</pre>

Most helpful comment

Hello,

someone had a similar issue, using highlightjs which does a similar job as Prism (check the lastest answer from LinusBorg).

I don't know if it's your case, but I guess you are updating your model (paste.content) from your Vue / Component instance and you are expecting the DOM to be updated and highlighted in the same time.

The problem is that Prism will update your model rendered value (raw string), then apply a lot of HTML transformations. Then VueJS won't be able to update the DOM. This is well described in the link I mentionned above :

  • Either it's real HTML DOM elements, and Vue handles them (then {{content}} works),
  • or it's supposed to be highlighted HTML markup, and highlightjs handles them underlying DOM elements that it needs to print it's highlighted text (then {{content}} won't work because highlightJS controls that DOM, not Vue).

One possible dirty solution is to update manually the DOM :

  • When your model changes, replace your code element content with your model value
  • Highlight your element on VueJS next tick

_EDIT:_

An exemple of code is :

watch: {
  // Note : paste.content is declared as string and assume paste is defined in Vue data
  'paste.content': function(value) {
    let editor = document.querySelector('#editor code');
    editor.innerHTML = value;
    this.$nextTick(()=> Prism.highlightElement(editor));
  }
}

One other option is to watch the paste object using the deep option which will help you to observe inner property change:

watch: {
  // Observe paste instance defined in Vue data using the deep option
  paste:  {
    handler(value) {
      let editor = document.querySelector('#editor code');
      editor.innerHTML = value;
      this.$nextTick(()=> Prism.highlightElement(editor));
    },
    deep: true
  }
}

Hope it will help you

All 10 comments

That doesn't work :/
(https://gyazo.com/b56294adf1375aed6156ee056da3ad21)

Hello,

someone had a similar issue, using highlightjs which does a similar job as Prism (check the lastest answer from LinusBorg).

I don't know if it's your case, but I guess you are updating your model (paste.content) from your Vue / Component instance and you are expecting the DOM to be updated and highlighted in the same time.

The problem is that Prism will update your model rendered value (raw string), then apply a lot of HTML transformations. Then VueJS won't be able to update the DOM. This is well described in the link I mentionned above :

  • Either it's real HTML DOM elements, and Vue handles them (then {{content}} works),
  • or it's supposed to be highlighted HTML markup, and highlightjs handles them underlying DOM elements that it needs to print it's highlighted text (then {{content}} won't work because highlightJS controls that DOM, not Vue).

One possible dirty solution is to update manually the DOM :

  • When your model changes, replace your code element content with your model value
  • Highlight your element on VueJS next tick

_EDIT:_

An exemple of code is :

watch: {
  // Note : paste.content is declared as string and assume paste is defined in Vue data
  'paste.content': function(value) {
    let editor = document.querySelector('#editor code');
    editor.innerHTML = value;
    this.$nextTick(()=> Prism.highlightElement(editor));
  }
}

One other option is to watch the paste object using the deep option which will help you to observe inner property change:

watch: {
  // Observe paste instance defined in Vue data using the deep option
  paste:  {
    handler(value) {
      let editor = document.querySelector('#editor code');
      editor.innerHTML = value;
      this.$nextTick(()=> Prism.highlightElement(editor));
    },
    deep: true
  }
}

Hope it will help you

It doesn't work for me, the [dot] between paste and content (paste.content) is unexpected.

Sorry, I bring some corrections. To watch paste inner property, the syntax should be 'paste.content': function(value)
Please check the previous edited answer.

@haryratsimba thank You so much! It works :)

I wonder if something like this would help, but where the mutation observer tracks the content of the <code> elements, not just new ones. Then every time Vue updates the <code> element, Prism automatically highlights it.

Solved using updated property on component:

  var Post = {
    data: function() {
      var postData = {
        post: ''
      };   
      var articleTitle = this.$route.path.replace('/', '');
      fetch(getPostUrl + articleTitle, {method: "GET", cache: "default"}).then(
        function(response) { 
          if (!response.ok) {
            console.log("bad response from server");
          }                  
          return response.json()
        }).then(
          function(responseData) {
            postData.post = responseData;
        }).catch(function(err) {
          console.log(err);
        });
      return postData
    },     
    template: `
      <div class="uk-card uk-card-body uk-card-default">
        <article class="uk-article">
          <h1 class="uk-article-title uk-text-center uk-text-uppercase">{{post.title}}</h1>
          <p class="uk-article-metadata uk-text-center" v-show="post.author">Written by {{post.author}} on {{post.timestamp}}</p>
          <hr class="uk-divider-icon" v-show="post.author">
          <p v-html="post.content"></p>
          <div class="uk-text-center">
            <div uk-spinner="ratio: 5" v-show="!post.author"></div>
          </div>
        </article>
      </div>
    `,     
    updated: function() {
      Prism.highlightAll();
    }      
  };  

@morissette I had the same problem. Do you know why it is ok to use updated property, but watch property isn't?

Solved using updated property on component:

  var Post = {
    data: function() {
      var postData = {
        post: ''
      };   
      var articleTitle = this.$route.path.replace('/', '');
      fetch(getPostUrl + articleTitle, {method: "GET", cache: "default"}).then(
        function(response) { 
          if (!response.ok) {
            console.log("bad response from server");
          }                  
          return response.json()
        }).then(
          function(responseData) {
            postData.post = responseData;
        }).catch(function(err) {
          console.log(err);
        });
      return postData
    },     
    template: `
      <div class="uk-card uk-card-body uk-card-default">
        <article class="uk-article">
          <h1 class="uk-article-title uk-text-center uk-text-uppercase">{{post.title}}</h1>
          <p class="uk-article-metadata uk-text-center" v-show="post.author">Written by {{post.author}} on {{post.timestamp}}</p>
          <hr class="uk-divider-icon" v-show="post.author">
          <p v-html="post.content"></p>
          <div class="uk-text-center">
            <div uk-spinner="ratio: 5" v-show="!post.author"></div>
          </div>
        </article>
      </div>
    `,     
    updated: function() {
      Prism.highlightAll();
    }      
  };  

thank mr.
i try like this :

import prism from "prismjs/prism";

computed: {
    highlightedCode() {
      return this.dataArtikel;
    },
  },
  updated: function () {
    prism.highlightAll();
  },

and my template :

<span v-html="highlightedCode"> </span>

sucsess, highlight code in v-html..

Was this page helpful?
0 / 5 - 0 ratings

Related issues

huweihuang picture huweihuang  路  6Comments

timgoeller picture timgoeller  路  4Comments

RunDevelopment picture RunDevelopment  路  7Comments

nterray picture nterray  路  4Comments

kizu picture kizu  路  7Comments