patternjavascriptMinor
Simple syntax highlighter in Javascript - SQL highlighting
Viewed 0 times
simplejavascriptsqlhighlightingsyntaxhighlighter
Problem
Yesterday and today I've made a very basic syntax highlight.
It creates a function in the
Each language is a property in the function, which is then read (sql example):
```
(function(window){
if('function' === typeof window.highlight)
{
window.highlight.sql=[
{
'class':'string',
'match':/([bn]?"(?:[^"]|[\\"]")"|[bn]?'(?:[^']|[\\']')')(?=[\b\s\(\),;\$#\+\-\*\/]|$)/g,
'replace':window.highlight.default_replace
},
{
'class':'comment',
'match':/((?:\/\/|\-\-\s|#)[^\r\n]|\/\(?:[^]|\[^\/])(?:\\/|$))/g,
'replace':window.
It creates a function in the
window object that handles part of the job:(function(window){
var f=window.highlight = function(lang, element){
var lang_defs = f[lang];
if(!lang_defs)
{
throw new TypeError( 'The language "' + lang + '" was not yet defined' );
}
if(!(element instanceof Element))
{
throw new TypeError( 'The 2nd parameter must be an Element' );
}
element.className += ' highlight ' + lang;
for(var i = 0, l = lang_defs.length; i' +
lang_defs[i].replace.text +
''
);
}
else
{
html += element.childNodes[j].outerHTML;
}
}
element.innerHTML = html;
if('function' === typeof lang_defs[i].patch)
{
var returned = lang_defs[i].patch.call( element );
if('string' === typeof returned)
{
element.innerHTML = returned;
}
}
}
};
//default replace object
f.default_replace = {'tag': 'span', 'text': '$1'};
})(Function('return this')());//just be sure that we have the real windowEach language is a property in the function, which is then read (sql example):
```
(function(window){
if('function' === typeof window.highlight)
{
window.highlight.sql=[
{
'class':'string',
'match':/([bn]?"(?:[^"]|[\\"]")"|[bn]?'(?:[^']|[\\']')')(?=[\b\s\(\),;\$#\+\-\*\/]|$)/g,
'replace':window.highlight.default_replace
},
{
'class':'comment',
'match':/((?:\/\/|\-\-\s|#)[^\r\n]|\/\(?:[^]|\[^\/])(?:\\/|$))/g,
'replace':window.
Solution
After closely watching the code, I realized that I've made a few mistakes:
Mistake 1:
The languages are being added directly as a property in the function. That's just begging for trouble!
I've added an object where all the languages will be added.
Mistake 2:
To generate the new HTML to search for the text nodes (the un-highlighted text), I was refreshing the element itself.
That means that all the styles related with that element were being refreshed over and over and over again.
Now, with a document fragment, the number of reflows was reduced to 2!
Mistake 3 (or 2.5?):
Since I was setting the class at the beggining, before any code, which was helping in the number of reflows.
This is a total waste of time for the CPU. This reflow was moved before doing any direct changes in the code, but before adding the new HTML.
Mistake 4:
The lack of 'stricness' disturbed some people.
And it is a good point!
Now,
Now, with all the mistakes sorted out, I've also made some changes:
Change 1:
The language now can be set directly in the element, or as an optional parameter in the function.
Change 2:
If you forget to set a language, both on the element and in the parameter, it throws a friendly exception.
Also, the exception about the language being missing was adjusted.
Change 3:
You can now pass a set of elements (
The exceptions that are thrown will be handled differently, giving the other elements a chance.
The individual results for each element are returned in the form of an array.
Change 4:
@Mast pointed out something very simple and small.
The initialization had the following line:
At first, it is completely unclear what
I've changed it's name to
After all the changes, this is the final result:
Example of an execution (same HTML and CSS):
`//main file, containing the main function
(function(window){
'use strict';
//fn keeps an internal reference to avoid problems with rewritting the window.highlight
var fn = window.highlight = function(element, lang){
'use strict';
if(element instanceof NodeList || element instanceof HTMLCollection)
{
for(var i = 0, l = element.length, results = []; i' +
lang_defs[i].replace.text +
''
);
}
else
{
html += div.childNodes[j].outerHTML;
}
}
//refreshes the HTML, before doing anything else
div.innerHTML = html;
if('function' === typeof lang_defs[i].patch)
{
var returned = lang_defs[i].patch.call( div );
if('string' === typeof returned)
{
div.innerHTML = returned;
}
}
}
//only change at the end, to avoid unnecessary reflows
element.className += ' highlight ' + lang;
element.innerHTML = div.innerHTML;
return true;
};
//default replace object
fn.default_replace = {'tag': 'span', 'text': '$1'};
//all the languages will be added here
fn.langs = {};
})(Function('return this')());//just be sure that we have the real window
//==========================================================
// sql syntax highlight, anothed different file
(function(window){
'use strict';
if('function' === typeof window.highlight)
{
window.highlight.langs.sql=[
{
'class':'string',
'match':/([bn]?"(?:[^"]|[\\"]")"|[bn]?'(?:[^']|[\\']')')(?=[\b\s\(\),;\$#
Mistake 1:
The languages are being added directly as a property in the function. That's just begging for trouble!
I've added an object where all the languages will be added.
Mistake 2:
To generate the new HTML to search for the text nodes (the un-highlighted text), I was refreshing the element itself.
That means that all the styles related with that element were being refreshed over and over and over again.
Now, with a document fragment, the number of reflows was reduced to 2!
Mistake 3 (or 2.5?):
Since I was setting the class at the beggining, before any code, which was helping in the number of reflows.
This is a total waste of time for the CPU. This reflow was moved before doing any direct changes in the code, but before adding the new HTML.
Mistake 4:
The lack of 'stricness' disturbed some people.
And it is a good point!
Now,
'use strict'; is present in the code.Now, with all the mistakes sorted out, I've also made some changes:
Change 1:
The language now can be set directly in the element, or as an optional parameter in the function.
Change 2:
If you forget to set a language, both on the element and in the parameter, it throws a friendly exception.
Also, the exception about the language being missing was adjusted.
Change 3:
You can now pass a set of elements (
NodeList or HTMLCollection) and the elements will be highlighted.The exceptions that are thrown will be handled differently, giving the other elements a chance.
The individual results for each element are returned in the form of an array.
Change 4:
@Mast pointed out something very simple and small.
The initialization had the following line:
var f=window.highlight = function(element, lang){//...At first, it is completely unclear what
f is doing.I've changed it's name to
fn, and added a description of what that variable is doing there.After all the changes, this is the final result:
(function(window){
'use strict';
//fn keeps an internal reference to avoid problems with rewritting the window.highlight
var fn = window.highlight = function(element, lang){
'use strict';
if(element instanceof NodeList || element instanceof HTMLCollection)
{
for(var i = 0, l = element.length, results = []; i' +
lang_defs[i].replace.text +
''
);
}
else
{
html += div.childNodes[j].outerHTML;
}
}
//refreshes the HTML, before doing anything else
div.innerHTML = html;
if('function' === typeof lang_defs[i].patch)
{
var returned = lang_defs[i].patch.call( div );
if('string' === typeof returned)
{
div.innerHTML = returned;
}
}
}
//only change at the end, to avoid unnecessary reflows
element.className += ' highlight ' + lang;
element.innerHTML = div.innerHTML;
return true;
};
//default replace object
fn.default_replace = {'tag': 'span', 'text': '$1'};
//all the languages will be added here
fn.langs = {};
})(Function('return this')());//just be sure that we have the real window
Example of an execution (same HTML and CSS):
`//main file, containing the main function
(function(window){
'use strict';
//fn keeps an internal reference to avoid problems with rewritting the window.highlight
var fn = window.highlight = function(element, lang){
'use strict';
if(element instanceof NodeList || element instanceof HTMLCollection)
{
for(var i = 0, l = element.length, results = []; i' +
lang_defs[i].replace.text +
''
);
}
else
{
html += div.childNodes[j].outerHTML;
}
}
//refreshes the HTML, before doing anything else
div.innerHTML = html;
if('function' === typeof lang_defs[i].patch)
{
var returned = lang_defs[i].patch.call( div );
if('string' === typeof returned)
{
div.innerHTML = returned;
}
}
}
//only change at the end, to avoid unnecessary reflows
element.className += ' highlight ' + lang;
element.innerHTML = div.innerHTML;
return true;
};
//default replace object
fn.default_replace = {'tag': 'span', 'text': '$1'};
//all the languages will be added here
fn.langs = {};
})(Function('return this')());//just be sure that we have the real window
//==========================================================
// sql syntax highlight, anothed different file
(function(window){
'use strict';
if('function' === typeof window.highlight)
{
window.highlight.langs.sql=[
{
'class':'string',
'match':/([bn]?"(?:[^"]|[\\"]")"|[bn]?'(?:[^']|[\\']')')(?=[\b\s\(\),;\$#
Code Snippets
var f=window.highlight = function(element, lang){//...Context
StackExchange Code Review Q#90938, answer score: 7
Revisions (0)
No revisions yet.