diff --git a/README.md b/README.md index b07da54..71e8ec0 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ $(document).ready(function() { If your needs cool style, add styles by taste: ```css /* for block of numbers */ -.hljs-ln td.hljs-ln-numbers { +.hljs-ln-numbers { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; @@ -70,7 +70,7 @@ If your needs cool style, add styles by taste: } /* for block of code */ -.hljs-ln td.hljs-ln-code { +.hljs-ln-code { padding-left: 10px; } ``` @@ -95,5 +95,19 @@ hljs.initLineNumbersOnLoad({ hljs.lineNumbersBlock(myCodeBlock, myOptions); ``` +## CSS selectors + +You may need to select some lines of code after rendering. For instance, you may want +to highlight a range of lines, selected by users, by changing their background color. +The CSS selectors below can be used to perform these selection operations. + +CSS selector | description +-----------------------------------------|----------------------- +`.hljs-ln-line` | Select all lines, including line numbers +`.hljs-ln-numbers` | Select all line numbers, excluding lines of code +`.hljs-ln-code` | Select all lines of code, excluding line numbers +`.hljs-ln-line[data-line-number="i"]` | Select the ith line, including line number +`.hljs-ln-numbers[data-line-number="i"]` | Select the ith line number, excluding the line of code +`.hljs-ln-code[data-line-number="i"]` | Select the ith line of code, excluding the line number --- © 2018 Yauheni Pakala | MIT License diff --git a/src/highlightjs-line-numbers.js b/src/highlightjs-line-numbers.js index b2613ff..7e7d3a8 100644 --- a/src/highlightjs-line-numbers.js +++ b/src/highlightjs-line-numbers.js @@ -5,12 +5,16 @@ var TABLE_NAME = 'hljs-ln', LINE_NAME = 'hljs-ln-line', + NUMBERS_CONTAINER_NAME = 'hljs-ln-numbers-container', + CODE_CONTAINER_NAME = 'hljs-ln-code-container', CODE_BLOCK_NAME = 'hljs-ln-code', NUMBERS_BLOCK_NAME = 'hljs-ln-numbers', NUMBER_LINE_NAME = 'hljs-ln-n', DATA_ATTR_NAME = 'data-line-number', BREAK_LINE_REGEXP = /\r\n|\r|\n/g; + var resizeHandlerSet = false; + if (w.hljs) { w.hljs.initLineNumbersOnLoad = initLineNumbersOnLoad; w.hljs.lineNumbersBlock = lineNumbersBlock; @@ -25,13 +29,19 @@ var css = d.createElement('style'); css.type = 'text/css'; css.innerHTML = format( - '.{0}{border-collapse:collapse}' + - '.{0} td{padding:0}' + - '.{1}:before{content:attr({2})}', + '.{0} table {float: left; border-collapse: collapse}' + + '.{0} table td {padding: 0}' + + '.{1}::before {content: attr({2})}' + + // force display of empty lines of code + '.{3}::before {content: "\\200B"}' + + // ensure consistent line heights in dom elements for numbers and code + '.{3},.{4} {line-height: 16px; line-height: 1rem}', [ TABLE_NAME, NUMBER_LINE_NAME, - DATA_ATTR_NAME + DATA_ATTR_NAME, + CODE_BLOCK_NAME, + NUMBERS_BLOCK_NAME ]); d.getElementsByTagName('head')[0].appendChild(css); } @@ -60,11 +70,44 @@ } } + function adjustLineNumbersHeights(element) { + var lnNumbers = element.querySelectorAll('.' + NUMBERS_BLOCK_NAME); + var lnCode = element.querySelectorAll('.' + CODE_BLOCK_NAME); + + for (var i = 0 ; i < lnNumbers.length; ++i) { + lnNumbers[i].style.height = lnCode[i].offsetHeight + 'px'; + } + } + function lineNumbersBlock (element, options) { if (typeof element !== 'object') return; - async(function () { element.innerHTML = lineNumbersInternal(element, options); + // adjust left margin of code div as line numbers is a float left dom element + var lineNumbersContainer = element.querySelector('.' + NUMBERS_CONTAINER_NAME); + if (lineNumbersContainer) { + var codeMargin = lineNumbersContainer.offsetWidth; + var codeContainerStyle = 'margin-left:' + codeMargin + 'px'; + var codeContainer = element.querySelector('.' + CODE_CONTAINER_NAME); + codeContainer.style.cssText = codeContainerStyle; + + // adjust each line number cell height to the one of the div containing + // the wrapped line of code and set a handler to execute this + // operation when the browser window gets resized. + // execute this operation asynchronously once the code has been rendered + async(adjustLineNumbersHeights(element)); + if (!resizeHandlerSet) { + window.addEventListener('resize', function() { + async(function() { + var hljsLnElts = document.querySelectorAll('.' + TABLE_NAME); + for (var i = 0 ; i < hljsLnElts.length; ++i) { + adjustLineNumbersHeights(hljsLnElts[i]); + } + }); + }); + resizeHandlerSet = true; + } + } }); } @@ -101,30 +144,57 @@ } if (lines.length > firstLineIndex) { - var html = ''; + // Previous implementation was using a single table element + // to render the line numbers and the lines of code. + // But to overcome an annoying copy/paste behavior when using Firefox or Edge + // (see https://github.com/wcoder/highlightjs-line-numbers.js/issues/51) + // the following workaround is used while obtaining the exact same rendering + // as before: + // 1. render the lines number in a table with single column + // 2. render the lines of code in a div + // 3. wrap these in a div and make the table float left + // 4. adjust the left margin of the code div once inserted in the dom + var htmlLinesNumber = ''; + var htmlCode = ''; for (var i = 0, l = lines.length; i < l; i++) { - html += format( - '