0

I have a VueJS where I have created a component for rendering the contents from a WYSIWYG component (tiptap).

I have the following content being returned from the backend

let x = 0;

enum A {}
function Baa() {}

I'm using highlight.js to highlight this code snippet in the following manner:

import { defineComponent, h, nextTick, onMounted, onUpdated, ref, watch } from 'vue';
// No need to use a third-party component to highlight code
// since the `@tiptap/extension-code-block-lowlight` library has highlight as a dependency
import highlight from 'highlight.js'

export const WYSIWYG = defineComponent({
  name: 'WYSIWYG',
  props: {
    content: { type: String, required: true },
  },
  setup(props) {
    const root = ref<HTMLElement>(null);

    const highlightClass = 'hljs';

    const hightlightCodes = async () => {
      console.log(root.value?.querySelectorAll('pre code')[0]);

      setTimeout(() => {
        root.value?.querySelectorAll('pre code').forEach((el: HTMLElement) => {
          highlight.highlightElement(el as HTMLElement);
        });
      }, 2000);
    }

    onMounted(hightlightCodes);
    watch(() => props.content, hightlightCodes);

    return function render() {
      return h('div', {
        class: 'WYSIWYG',
        ref: root,
        innerHTML: props.content
      });
    };
  },
});

Now, when I visit the page by typing the URL in the browser, it highlights the typescript code

enter image description here

Whenever I visit a different page and click on my browser's "Go back" button, it makes the code completely vanishes

enter image description here

What I have tried

I can see that the line root.value?.querySelectorAll('pre code') is returning the correct items and the correct code is present but the code vanishes after the 2 seconds passes - due to setTimeout.

How can I make highlight.js highlight the code parts whenever props.content changes?

2 Answers 2

0

Option 1

Use Highlight.js Vue integration (you need to setup the plugin first, check the link):

<script setup>
const props = defineProps({
  content: { type: String, required: true },
})
</script>

<template>
  <highlightjs :code="content" language="ts" />
</template>

Option 2

  • Use computed to reactively compute highlighted HTML of props.content
  • Use sync highlight(code, options) function to get the highlighted HTML
  • Use HTML as-is via innerHTML prop or v-html directive
<script setup>
import { computed } from 'vue'
import highlight from 'highlight.js'

const props = defineProps({
  content: { type: String, required: true },
})

const html = computed(() => {
  const { value } = highlight.highlight(props.content, { lang: 'ts' })
  return value
})
</script>

<template>
  <div v-html="html" />
</template>
Sign up to request clarification or add additional context in comments.

1 Comment

The problem is that the content is coming from the backend and it can contain other things besides just code. An example <h1> This is a title </h1> <pre><code class="language-ts"> enum Foo {} </code></pre>. Any way around this?
0

In template you have to call a method instead of delivering to v-html your text code:

import hljs from 'highlight.js/lib/core'; 
import ts from 'highlight.js/lib/languages/typescript'
import { decode } from 'he';

hljs.registerLanguage('ts', ts);
const formattedText = (text) => {
   if (text && text != null) {
      return highlightCodeBlocks(text);
   } else return '';
};

const highlightCodeBlocks = (text) => {
   const codeBlockRegex = /<pre><code(?: class="language-(\w+)")?>([\s\S]*?)<\/code><\/pre>/g;
        
   return text.replace(codeBlockRegex, (match, lang, code) => {
      code = decode(code)
      if (lang && hljs.getLanguage(lang)) {
         return `<pre><code class="language-${lang}">${hljs.highlight(code, {language: lang, ignoreIllegals: true}).value}</code></pre>`;
      } else {
         // Automatically detect the language for code without a specified language
         const autoDetected = hljs.highlightAuto(code);
         return `<pre><code class="language-${autoDetected.language}">${autoDetected.value}</code></pre>`;
      }
   });
};`

template: <span class="" v-html="formattedText(text)">

use 'he' if you want to highlight html code = decode(code)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.