boreq
boreq

Reputation: 840

How to reinitialize highlight.js?

My website is generating some content dynamically, so I have to somehow launch the highlight.js plugin again after loading it.

This code is used to launch the highlighter:

hljs.initHighlightingOnLoad();

I tried to do something like hljs.initHighlighting(); to do this again but it does not work.

Upvotes: 23

Views: 12499

Answers (6)

Lukas
Lukas

Reputation: 409

My code editor uses highlight.js but should be editable so after each editing it calls hljs.highlightElement(this); Here`s the code:

<html><head>
    <title>Python Editor</title>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css" rel="stylesheet">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js" type="text/javascript"></script>
    <script type="text/javascript">hljs.highlightAll();
                 function replaceTagMarks(str){
                     //to ensure code is not interpreted as HTML
                     return str.replaceAll('<', '&lt').replaceAll('>', '&gt');
                 }
                 function removeHighLight(el){
                     //innerText contains the plain text without the surounding html made by hljs 
                     el.innerHTML = replaceTagMarks(el.innerText);
                     //to inform hljs that the element is no longer highlighted 
                     delete el.dataset.highlighted;
                 }
    </script>
  </head>
  <body>
  <pre><code contenteditable = 'true'
      class='language-python hljs'
      spellcheck = 'false',
      onfocusout = 'hljs.highlightElement(this);',
      onfocusin = 'removeHighLight(this);')>
      print('Hello world')
  </code></pre>
  <body>
</html>

Upvotes: 0

ggorlen
ggorlen

Reputation: 56905

Calling hljs.highlightAll() or hljs.highlightElement(e) multiple times on already-highlighted elements results in an annoying warning:

Element previously highlighted. To highlight again, first unset dataset.highlighted.

hljs.highlightAll();
setTimeout(() => hljs.highlightAll(), 0);
<link
  rel="stylesheet"
  href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/vs2015.min.css"
/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<pre><code>console.log("hello" + 42);</code></pre>

But unsetting dataset.highlighted doesn't really seem to work, unless I'm missing something, because it doesn't remove the HTML elements hljs added to facilitate its highlighting:

const highlightAll = () => {
  document.querySelectorAll("code").forEach((e) => {
    delete e.dataset.highlighted;
    hljs.highlightElement(e);
  });

  // fails too when used instead of highlightElement:
  // hljs.highlightAll();
};

highlightAll();
setTimeout(highlightAll, 0);
<link
  rel="stylesheet"
  href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/vs2015.min.css"
/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<pre><code>console.log("hello" + 42);</code></pre>

An approach that circumvents this is to skip highlighting anything that's already highlighted:

const highlightAll = () => {
  document.querySelectorAll("code").forEach((e) => {
    if (!e.dataset.highlighted) {
      hljs.highlightElement(e);
    }
  });
};

highlightAll();
setTimeout(highlightAll, 0);
<link
  rel="stylesheet"
  href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/vs2015.min.css"
/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<pre><code>console.log("hello" + 42);</code></pre>

As far as I can tell, if you need to re-highlight rather than skip, you'll need to replace all of the HTML with the original text content before calling hljs.highlightElement(element).

Upvotes: 1

Fuadit Muhammad
Fuadit Muhammad

Reputation: 151

If you using react hooks please follow this;

I get the solution by integrate react-highlight.

You can use innerHtml to true on the property.

import Highlight from "react-highlight"
import "highlight.js/styles/gradient-dark.css"; // import style on the root file
import JSONViewer from "./JSONViewer"

<Highlight innerHTML className="w-full">
  <JSONViewer content={yourContentHere} />
</Highlight>

Set innerHTML=true to highlight multiple code snippets at a time. This is especially usefull if html with multiple code snippets is generated from preprocesser tools like markdown.

Warning: If innerHTML is set to true, make sure the html generated with code snippets is from trusted source.

Trying to forcing it on useEffect?

Using hljs.registerLanguage("json", json); or hljs.initHighlightingOnLoad(); will getting a problem by rerendering react. It's not recall when using dependency but useEffect without dependency like this it works.

useEffect(() => {
  hljs.initHighlightingOnLoad(); // it works, but bad for performance because will call every single time re-rendering.
})

useEffect(() => {
  hljs.initHighlightingOnLoad(); // it doesn't works
}, [])

Upvotes: 0

Handi
Handi

Reputation: 67

i hope this could solve your problem.. you have to use

hljs.highlightAll()

since, hljs.initHighlightingOnLoad() is deprecated since version 10.6.

if you're using react, you could apply at componentdidMount..

  useEffect(() => {
    hljs.highlightAll()
  }, []);

or, if another framework, please call that function when page loaded. more: https://highlightjs.readthedocs.io/en/latest/api.html

Upvotes: 0

Serhii Polishchuk
Serhii Polishchuk

Reputation: 1508

You must set called to false first:

hljs.initHighlighting.called = false;
hljs.initHighlighting();

Upvotes: 47

planetoftheweb
planetoftheweb

Reputation: 222

You can reinitialize all of the codeblocks like this.

$(document).ready(function() {
   $('pre code').each(function(i, e) {hljs.highlightBlock(e)});
});

or if you have a div with an ID of myBlock, you can do this.

$(document).ready(function() {
   $('#myBlock').each(function(i, e) {hljs.highlightBlock(e)});
});

Upvotes: 6

Related Questions