Skip to content

Commit 3ba229d

Browse files
committed
Fix copy/paste behavior when using Firefox or Edge (#51)
1 parent f4bfce4 commit 3ba229d

File tree

1 file changed

+85
-15
lines changed

1 file changed

+85
-15
lines changed

src/highlightjs-line-numbers.js

Lines changed: 85 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,16 @@
55

66
var TABLE_NAME = 'hljs-ln',
77
LINE_NAME = 'hljs-ln-line',
8+
NUMBERS_CONTAINER_NAME = 'hljs-ln-numbers-container',
9+
CODE_CONTAINER_NAME = 'hljs-ln-code-container',
810
CODE_BLOCK_NAME = 'hljs-ln-code',
911
NUMBERS_BLOCK_NAME = 'hljs-ln-numbers',
1012
NUMBER_LINE_NAME = 'hljs-ln-n',
1113
DATA_ATTR_NAME = 'data-line-number',
1214
BREAK_LINE_REGEXP = /\r\n|\r|\n/g;
1315

16+
var resizeHandlerSet = false;
17+
1418
if (w.hljs) {
1519
w.hljs.initLineNumbersOnLoad = initLineNumbersOnLoad;
1620
w.hljs.lineNumbersBlock = lineNumbersBlock;
@@ -25,13 +29,19 @@
2529
var css = d.createElement('style');
2630
css.type = 'text/css';
2731
css.innerHTML = format(
28-
'.{0}{border-collapse:collapse}' +
29-
'.{0} td{padding:0}' +
30-
'.{1}:before{content:attr({2})}',
32+
'.{0} table {float: left; border-collapse: collapse}' +
33+
'.{0} table td {padding: 0}' +
34+
'.{1}::before {content: attr({2})}' +
35+
// force display of empty lines of code
36+
'.{3}::before {content: "\\200B"}' +
37+
// ensure consistent line heights in dom elements for numbers and code
38+
'.{3},.{4} {line-height: 16px; line-height: 1rem}',
3139
[
3240
TABLE_NAME,
3341
NUMBER_LINE_NAME,
34-
DATA_ATTR_NAME
42+
DATA_ATTR_NAME,
43+
CODE_BLOCK_NAME,
44+
NUMBERS_BLOCK_NAME
3545
]);
3646
d.getElementsByTagName('head')[0].appendChild(css);
3747
}
@@ -60,11 +70,44 @@
6070
}
6171
}
6272

73+
function adjustLineNumbersHeights(element) {
74+
var lnNumbers = element.querySelectorAll('.' + NUMBERS_BLOCK_NAME);
75+
var lnCode = element.querySelectorAll('.' + CODE_BLOCK_NAME);
76+
77+
for (var i = 0 ; i < lnNumbers.length; ++i) {
78+
lnNumbers[i].style.height = lnCode[i].offsetHeight + 'px';
79+
}
80+
}
81+
6382
function lineNumbersBlock (element, options) {
6483
if (typeof element !== 'object') return;
65-
6684
async(function () {
6785
element.innerHTML = lineNumbersInternal(element, options);
86+
// adjust left margin of code div as line numbers is a float left dom element
87+
var lineNumbersContainer = element.querySelector('.' + NUMBERS_CONTAINER_NAME);
88+
if (lineNumbersContainer) {
89+
var codeMargin = lineNumbersContainer.offsetWidth;
90+
var codeContainerStyle = 'margin-left:' + codeMargin + 'px';
91+
var codeContainer = element.querySelector('.' + CODE_CONTAINER_NAME);
92+
codeContainer.style.cssText = codeContainerStyle;
93+
94+
// adjust each line number cell height to the one of the div containing
95+
// the wrapped line of code and set a handler to execute this
96+
// operation when the browser window gets resized.
97+
// execute this operation asynchronously once the code has beed rendered
98+
async(adjustLineNumbersHeights(element));
99+
if (!resizeHandlerSet) {
100+
window.addEventListener('resize', function() {
101+
async(function() {
102+
var hljsLnElts = document.querySelectorAll('.' + TABLE_NAME);
103+
for (var i = 0 ; i < hljsLnElts.length; ++i) {
104+
adjustLineNumbersHeights(hljsLnElts[i]);
105+
}
106+
});
107+
});
108+
resizeHandlerSet = true;
109+
}
110+
}
68111
});
69112
}
70113

@@ -101,30 +144,57 @@
101144
}
102145

103146
if (lines.length > firstLineIndex) {
104-
var html = '';
147+
// Previous implementation was using a single table element
148+
// to render the line numbers and the lines of code.
149+
// But to overcome an annoying copy/paste behavior when using Firefox or Edge
150+
// (see https://github.com/wcoder/highlightjs-line-numbers.js/issues/51)
151+
// the following workaround is used while obtaining the exact same rendering
152+
// as before:
153+
// 1. render the lines number in a table with single column
154+
// 2. render the lines of code in a div
155+
// 3. wrap these in a div and make the table float left
156+
// 4. adjust the left margin of the code div once inserted in the dom
157+
var htmlLinesNumber = '';
158+
var htmlCode = '';
105159

106160
for (var i = 0, l = lines.length; i < l; i++) {
107-
html += format(
108-
'<tr>' +
109-
'<td class="{0}">' +
110-
'<div class="{1} {2}" {3}="{5}"></div>' +
111-
'</td>' +
112-
'<td class="{4}">' +
113-
'<div class="{1}">{6}</div>' +
161+
htmlLinesNumber += format(
162+
'<tr class="{0} {1}" {3}="{4}">' +
163+
'<td>' +
164+
'<div class="{2}" {3}="{4}"></div>' +
114165
'</td>' +
115166
'</tr>',
116167
[
117-
NUMBERS_BLOCK_NAME,
118168
LINE_NAME,
169+
NUMBERS_BLOCK_NAME,
119170
NUMBER_LINE_NAME,
120171
DATA_ATTR_NAME,
172+
i + 1
173+
]);
174+
175+
htmlCode += format(
176+
'<div class="{0} {1}" {2}="{3}">{4}</div>',
177+
[
178+
LINE_NAME,
121179
CODE_BLOCK_NAME,
180+
DATA_ATTR_NAME,
122181
i + 1,
123182
lines[i].length > 0 ? lines[i] : ' '
124183
]);
125184
}
126185

127-
return format('<table class="{0}">{1}</table>', [ TABLE_NAME, html ]);
186+
return format(
187+
'<div class="{0}">' +
188+
'<table class="{1}">{2}</table>' +
189+
'<div class="{3}">{4}</div>' +
190+
'</div>',
191+
[
192+
TABLE_NAME,
193+
NUMBERS_CONTAINER_NAME,
194+
htmlLinesNumber,
195+
CODE_CONTAINER_NAME,
196+
htmlCode
197+
]);
128198
}
129199

130200
return inputHtml;

0 commit comments

Comments
 (0)