The other day I was working on a JSON Schema Generator, and wanted to display line numbers in a
I did some research, and found multiple approaches:
I did not like any of them! The first one didn't look crisp — and didn't match the styles I already had in place for my
The second one required a bunch of JavaScript to maintain that ordered list: adding/removing
So I ended up creating a hybrid.
It's a dynamically generated SVG, stored as a CSS Custom Property — and used as a background-image, inheriting the styles from it's parent
Let's dive in.
First, the main method:
lineNumbers(element, numLines = 50, inline = false)
element is the
Next, we define a prefix for the custom property:
const prefix = '--linenum-';
Before we continue, we check whether to re-use any existing property:
if (!inline) { const styleString = document.body.getAttribute('style') || ''; const regex = new RegExp(`${prefix}[^:]*`, 'g'); const match = styleString.match(regex); if (match) { element.style.backgroundImage = `var(${match[0]})`; return; } }
Next, we extract styles from element, rendering the SVG with the same font-family, font-size, line-height etc. :
const bgColor = getComputedStyle(element).borderColor; const fillColor = getComputedStyle(element).color; const fontFamily = getComputedStyle(element).fontFamily; const fontSize = parseFloat(getComputedStyle(element).fontSize); const lineHeight = parseFloat(getComputedStyle(element).lineHeight) / fontSize; const paddingTop = parseFloat(getComputedStyle(element).paddingTop) / 2; const translateY = (fontSize * lineHeight).toFixed(2);
We need a random id for our property as well:
const id = `${prefix}${Math.random().toString(36).substr(2, 6)}`;
And now it's time to render the SVG:
const svg = ``;
Let's break it down:
In the
The last part iterates an array created from numLines, and appends the
We're almost there!
To use the generated SVG as a url()-property, we need to encode it:
const encodedURI = `url("data:image/svg xml,${encodeURIComponent(svg)}")`;
And finally, we set that property on either element or document-body:
const target = inline ? element : document.body; target.style.setProperty(id, encodedURI); element.style.backgroundImage = `var(${id})`;
And that's it!
Not too bad, and only 610 bytes, minified and compressed!
You can see a demo here, and dowload the full script here.
Below is a simplified Codepen, not using the inline-property logic:
Are there pros and cons? Of course there are!
Personally — for my current project — I needed a simple, crisp way of adding line numbers to a JSON-preview within a
This method does not rely on manipulating the DOM. The line numbers are generated as a single SVG, stored within a CSS Custom Property.
Since the line numbers are part of the background image, they automatically scroll with the text content, eliminating the need for manual synchronization logic.
By storing the generated SVG in a CSS Custom Property, it can be reused across multiple elements. This means that if several elements require the same line numbers, they can all reference the same custom property, avoiding redundant SVG generation.
Ordered lists are more accessible to screen readers and assistive technologies, while SVG-based line numbers might be ignored or misinterpreted.
Styling and interacting with individual line numbers in an ordered list is straightforward. In contrast, the SVG approach makes it more difficult to customize or add interactivity to specific line numbers.
SVG and CSS custom properties might not render consistently across all browsers — the current implementation has issues with Safari, where we need to deduct (paddingTop / 10) from translateY.
Ordered lists can be more flexible for handling dynamic content updates, such as adding or removing lines, whereas the SVG approach might require regenerating and reapplying the entire background image.
Disclaimer: All resources provided are partly from the Internet. If there is any infringement of your copyright or other rights and interests, please explain the detailed reasons and provide proof of copyright or rights and interests and then send it to the email: [email protected] We will handle it for you as soon as possible.
Copyright© 2022 湘ICP备2022001581号-3