Implementing highlight.js into an existing site

Why is this elements height returning 0?

(function()
{
  var height;
  if(typeof(window.getComputedStyle) == 'undefined') 
  { 
    return; 
  }
  var pre = document.getElementsByTagName('pre');
  for(var len = pre.length, i = 0; i < len; i ++)
  {
    var code = pre[i].getElementsByTagName('code').item(0);

    var column = document.createElement('div');
    column.setAttribute('aria-hidden', 'true');
    for(var n = 0; n < code.innerHTML.split(/[\n\r]/g).length; n ++)
    {
      height=column.appendChild(document.createElement('span'));

      console.log(height.offsetHeight);
    }
    pre[i].insertBefore(column, code);
    pre[i].className = 'line-numbers';
  }	
})();

I kept getting deeper and deeper into this Javascript code and eventuallly scrapped the whole thing. Spent about 3 hours on it.

I’m really hating the lines wrapping without adding a number. Could anyone please look at post 12 and help?

Basically, go to codefundamentals.com. CLick an article.

The actual code section you see is comprised of spans. Basically, I took the left columns numbers and got the length of it. Then I got the height of the numbers. Basically, if the height of the numbers were less than the total parents height, then there were lines that need adding.

So I took the leftover number from the math, divided it by one of the numbers height, and got that remainder. Added that many.

THis was all very confusing code and I do not feel ashamed by scrapping it. Anyway, could someone sort of take my logic, refine it, and show me where I can go from here?

Something of what I was dealing with…Note this is probably a bit broken due to my last attempts.

(function()
{
  if(typeof(window.getComputedStyle) == 'undefined') 
  { 
    return; 
  }
  var pre = document.getElementsByTagName('pre');
  for(var len = pre.length, i = 0; i < len; i ++)
  {
    var code = pre[i].getElementsByTagName('code').item(0);

    var column = document.createElement('div');
    column.setAttribute('class', 'number-lines');
    column.setAttribute('aria-hidden', 'true');
    for(var n = 0; n < code.innerHTML.split(/[\n\r]/g).length; n ++)
    {
      column.appendChild(document.createElement('span'));
    }
    pre[i].insertBefore(column, code);
    pre[i].className = 'line-numbers';
  }
  var child=document.getElementsByClassName('number-lines').item(0);
  var child2=child.getElementsByTagName('span');
  var child2List=Array.prototype.slice.call(child2);
  var eleH=0;

  var create;
  var before;
  var pre2=document.getElementsByTagName('pre');
  var total;
  for(var i=0;i<=child2List.length;i++)
  {
    eleH+=child2List[i].offsetHeight;
    if(i==child2List.length-1)
    {
      if(eleH<child.offsetHeight)
      {
        total=child.offsetHeight-eleH;
        create=document.getElementsByClassName('number-lines').item(0);
        while(total>0)
        {
          for(var len = pre.length, i = 0; i < len; i ++)
          {
  var code2= pre[i].getElementsByTagName('code').item(0);
  var column2 = document.createElement('div');
 before=create.appendChild(document.createElement('span'));
          total=total-child2List[0].offsetHeight;
        }
      }
    }
  }
})();

It’s hideous, I know.

Last post I promise.

Decided to just double teh amount of spans I’m inputting, so it’s 2x spans per span node found (yea yea, inoptimal) and then I just use nifty CSS to hide the leftovers (extras that I don’t need) from appearing. Band-aid fixing so sue me. IT WORKS THOUGH. Time for dinner…

Glad you got it working. Sometimes we have to get to the ugly version of it before we can see a better alternative I still haven’t found one that I’m thrilled about, but at least you were able to get to a working one! (I’ve yet to do that)

Hi Ryan,

So, did you get this sorted to your satisfaction in the meantime?

1 Like

These are my issues
http://www.codefundamentals.com/test

Look at the code blocks. Ok, so the numbrers on the left. Right now it’s supposed to, for each node on the right, add a span (which CSSthen auto counts). Please look at .http://www.codefundamentals.com/scripts/code-counter.js

I just double the amount of inputted spans . Every line counter requires that you scroll the line instead of it wrapping. I need my counter to detect whether the line has wrapped and add additional spans accordingly. Right now, yes it works, however I add unnecessary spans. Go in inspect element to see I have almost double needed.

Now, also, look at example 2. This highlightjs I’m using only detects one language per pre/code block. I put two languages per one code block. Is there any way to modify highlightjs to sort of “re look” what language it is (or rather, re-detect) and highlight accordingly?

Now, the third example uses ONE pre, but two separate code blocks inside. That’s sort of fine although it ruins my contenteditable attribute (try clicking each language to see you get different editable areas).

Not quite a good solution. The issue we were having was we’d try the two separate code blcoks and it’d reset the numbers. You don’t see it here because with double the amount of spans needed (my band-aid fix) you don’t see it.

So with the number resetting in example 3, it would go 1-15, then when the HTML started…it’d go back to #1…So the line number needs to correctly detect whether it has wrapped, add accordingly, and depending on how highlightjs will be handled, get that numbering right.

Hi,

Sorry for the delayed replies - I’m travelling this week and not always in front of a computer.

Could I ask what you want to use highlight.js for? Is it for code formatting on your blog? (apologies if you mentioned it in the thread already)

For example the issue with two languages in one code block could be solved by using two code blocks, but I guess there is a reason you don’t want to do this.

It’s for code formatting for my blog, yes.

I could use two code blocks, but then that would make my contenteditable=true situation really weird looking. If I had two blocks, HTML and CSS for both, then whne I click HTML, I’d get an outline around that code, and not around the CSS.

I mean I could just remove that attribute since it’s not a big deal.

The main issue I’m having is getting the line numbers to calculate correctly. Prism.js’s line-numbers script (along with every other) assumes that you want to scroll the data in a horizontal scrollbar. I do not want that. However when it does wrap (as I want), the numbers don’t match the actual lines of data since 3x lines of data is only getting 1 number (for example.)

I band-aid fixed this by just doubling the amount of spans that I’m inputting, and hiding the rest via CSS. Very band-aidy fix but it worked. I’d rather get it properly coded though. The highlight.js issue will need to be tackled secondly probably (and honestly I might just remove that attribute and have two code blocks inside of my PRE.)

What am I doing wrong here? No numbers are generated. It says the .innerHTML is null. Not sure why.

var len, pre, code, column, h, i, j;
window.onload=function()
{
  lineNumbers();
}

var lineNumbers=function()
{
  "use strict";
  pre=document.getElementsByTagName('pre');
  for(h=0;h<=pre.length;h++)
  {
    code = pre.item(h).getElementsByTagName('code');
    len=code.length;
    for(i=0;i<len;i++)
    {
      column = document.createElement('div');
      column.setAttribute('aria-hidden', 'true');
      for(j=0;j<code.item(j).innerHTML.split(/[\n\r]/g).length;j++)
      {
        column.appendChild(document.createElement('span'));
      }
      code[i].insertBefore(column, code);
      code[i].className = 'line-numbers';
    }
  }
};

Managed to sort this out :slight_smile: . All I have left to do is detect wrapping and add additional spans.

EDIT-Old code was posted

I have test.php up on my website for this test (codefundamentals.com/test)

I was thinking of detecting each nodes height (in my var j for loop) and if it’s not equal to the rest (or perhaps have a constant, like the first node) then I’ll divide that and it’ll either be 2,3,4,etc times big. That’s how many I add to my counter. Thoughts? What would be the best way to determine wrapping?

Feels good to have this fixed properly so far. I ended up removing the contenteditable attributes and using two code blcoks inside of my PRE to get the highlighting right. Just had to update the linecounter script to change its criteria.

Not sure how I pasted that old code. It’s this.

"use strict";

var pre, code, h, len, column, linesOfCode, i, j;

pre=document.getElementsByTagName('pre');
for(h=0;h<pre.length;h++)
{
  code = pre.item(h).getElementsByTagName('code');
  len = code.length;
  column = document.createElement('div');
  column.setAttribute('aria-hidden', 'true');
  linesOfCode = 0;
	
  for(i=0;i<len;i++)
  {
    linesOfCode += code.item(i).innerHTML.split(/[\n\r]/g).length;
  }
  for(j=0;j<linesOfCode;j++)
  {
    column.appendChild(document.createElement('span'));
  }
  pre[h].className = 'line-numbers';
  pre[h].insertBefore(column, code[i]);	
}

The only way I am aware of is to find an element which you are sure doesn’t wrap and compare the height of all other elements to the height of this one.

This SO discussion might help: http://stackoverflow.com/questions/4575787/javascript-how-can-i-tell-if-a-span-is-broken-over-2-lines

Also, if you are not fixed on using prism.js, this looks like it might be interesting: http://stackoverflow.com/questions/6286733/automatic-line-break-in-js-syntaxhighlighter

Yeah that’s the way I was leaning towards things. I’ll try this and get back to you Pullo.

I’m having a little brain fart as to how to proceed with this. Can’t seem to get anything working. The console is giving me “0” when I’m looping out data.

http://www.codefundamentals.com/test.php

I’m trying to push my nodes to nodeHeights where I can then loop through the heights and figure out how much to add to lineNumbers. @James_Hibbard

Hey Ryan,

This actually turns out to be a little more problematic than anticipated.
I’m still working on this, but so far the only way I could get it to behave as desired was to configure highlight.js to break on <br> elements, then iterate over these <br> elements, get the height of the previous child, then adjust the height of the line numbers accordingly.
This works, but is incredibly hackish.

If you wish, I can let you have this code as is, or I can carry on playing around with it later and see if I can’t come up with something a little more elegant.

Perhaps we can take a different approach Pullo. This way wouldn’t be optimal but what are your thoughts.

After the first initial appending, we will have x amount of spans there taking up y amount of height.

Let’s say I have 12 lines of code, but only 10 spans. Those 10 spans are 20px height (I can find the first nodes height as a baseline.)

Now, that will be 200px of span height so far. If I have 12 lines of code, since the line-height/font etc is all the same, my code tag will be 248px tall (240 from the lines, 8px margin I set.)

So I could do some math to determine that I need 2 more from there and then do another append. Perhaps the appending could be put into a function and just call 2 more (in this case.) The first appending is going off the lineNumbers variable so I think if we wanted to move that to a function we could…anyway…What are your thoughts? WOuld this be easier?

Ah right, so if the code is ten lines long, but one of those ten lines wraps over two lines (because the individual line is longer than the container), then you would like the line numbers to run from 1-11. Did I get that right?

This is in contrast to your latest demo, where we have that situation, but the numbering stops at 10.

I had been thinking that you wanted the line numbers to run from 1-10, and to skip the lines where a long line had been wrapped (which was what I had working).

[quote=“James_Hibbard, post:39, topic:116981”]
Ah right, so if the code is ten lines long, but one of those ten lines wraps over three lines (because it is too long), then you would like the line numbers to run from 1-12. Did I get that right?
[/quote]Yes, sorry for being confusing.