Is it possible to get the value of a matched RegEx? If so, how should it be done?

Here is the code for a small syntax highlighter I made. It’s supposed to search through every code tag in my .html file and make highlights to certain parts of it. Once this runs in the browser, I get this weird "keyword"> text in front of every line I have a keyword in. After some debugging, I still haven’t came across a solution or a reason to why this problem occurs. I did notice that when I removed the ids from the code tag, the "keyword"> disappears, but the text still doesn’t get highlighted.

If I code everything manually rather than using JavaScript to do it for me, everything works fine, but the problem is that the process is very time consuming and I have many more code snippets to do this for. Is there any way that I can get the matching keyword from keywords and include it in somehow? Is there a way to iterate over regular expressions like in arrays? Thanks in advance 🙂

image 1 (partial success): image1 (partial success) I had to manually add span tags (removed unwanted text), but then highlighting doesn’t work

image 2 (error image): image2(error image)

CSS code (inline)

.keyword {color: #c792ed;}
.identifier {color: #a6acce;}
.string {color: #6401e5;}
.int {color: #f78c6a;}
.comment {color: #69aa00;}
/*
#code0, #code1
^-----> ^-----> not defined
*/

Html code

<p>
    <code id="code0">
        let text = "I'd like 2 pizzas please"; // This is a string. It holds text characters
    </code>
    <br>
    <code id="code1">
        let ovenOn = true; // This is a Boolean. Its values are either 'true' or 'false'.
    </code>
</p>

JavaScript code (inline)

let d = document;
let i = 0;
//----------------> Defining variables

       
function highlight() {
    let tags = d.getElementsByTagName("code");
    let reserved = "abstract arguments await boolean break byte case catch char class const continue debugger default delete do double else enum eval export extends false final finally float for function goto if implements import in instanceof int interface let long native new null package private protected public return short static super switch synchronized this throw throws transient true try typeof var void volatile while with yield";

    for(i; i<tags.length; i++){
        let code = tags[i].innerHTML;
        let keywords = new RegExp(reserved.split(' ').join('|'));
        //  ^------- keywords = ["abstract", "await", "etc..."]
        //           ^------- then becomes: keywords = /abstract|await|etc.../

        code = code.replace(keywords,   '<span class="keyword">$1</span>');
        //     ^---> Should capture the keyword in keywords (fails)
        //     ^---> see image2 

        code = code.replace(/"(.*?)"/g, '<span class="string">&quot;$1&quot;</span>');
        //     ^---> captures strings and places contents between quotes (works)

        code = code.replace(///(.*)/g,'<span class="comment">// $1 </span>');
        //     ^---> captures single-line comments (works)
        tags[i].innerHTML = code;
    }
}

window.addEventListener('load', highlight);
// ^-----> Calling highlight();

Answer

I fixed some things in the function.

function highlight() {
  let tags = d.getElementsByTagName("code");

  // I moved the reserved and keyword variables out the for loop so it will not be reinitialized for every code element

  // Its best to wrap the reserved words in word boundaries so if the reserved word is inside another word, it won't get replaced.
  // Example: 'with' as a reserved word will affect 'without'
  let reserved = "\b(abstract arguments await Boolean break byte case catch char class const continue debugger default delete do double else enum eval export extends false final finally float for function goto if implements import in instanceof int interface let long native new null package private protected public return short static super switch synchronized this throw throws transient true try typeof var void volatile while with yield)\b";

  //'I passed a global flag to the RegExp so all occurences of a reserved word will be replaced'
  const keywords = new RegExp(reserved.split(' ').join('|'), 'g');

  // The for loop doesn't run because i wasn't initialized.
  for (let i = 0; i < tags.length; i++) {
    let code = tags[i];

    code = code.replace(keywords, `<span class="keyword">$&</span>`);

    // After it the matches and replaces the reserved word, the class of the span tag is wrapped in quotes, so i fixed the regex to skip any match that is wrapped in the html tags <>
    code = code.replace(/(?!.*>)"(.*?)"/g, '<span class="string">"$1"</span>');

    code = code.replace(///(.*)/g, '<span class="comment">// $1 </span>');
    tags[i].innerHTML = code;
  }
}


// One of your examples should return this.
// <span class="keyword">let</span> text = <span class="string">"I'd like 2 pizzas please"</span>; <span class="comment">//  This is a string. It holds text characters</span>

Leave a Reply

Your email address will not be published. Required fields are marked *