"If a worker wants to do his job well, he must first sharpen his tools." - Confucius, "The Analects of Confucius. Lu Linggong"
Front page > Programming > Line Numbers for using SVG

Line Numbers for using SVG

Published on 2024-08-23
Browse:684

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:

  1. Using a background-image (TinyMCE does that, using a PNG)
  2. Using an
      ordered list.

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

  • -elements dynamically, syncing scroll-events and much more.

    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

    Line Numbers for <textarea> using SVG

    Let's dive in.


    JavaScript

    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 = `
      ${Array.from({ length: numLines }, (_, i) => `${i   1}`).join("")}
    `;
    

    Let's break it down:

    In the

    The last part iterates an array created from numLines, and appends the -elements to the main SVG.

    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!


    Demo

    You can see a demo here, and dowload the full script here.

    Below is a simplified Codepen, not using the inline-property logic:


    Pros and Cons

    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

    Pros

    Reduced DOM Manipulation

    This method does not rely on manipulating the DOM. The line numbers are generated as a single SVG, stored within a CSS Custom Property.

    Automatic Synchronization

    Since the line numbers are part of the background image, they automatically scroll with the text content, eliminating the need for manual synchronization logic.

    Reusability Across Elements

    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.

    Scalability

    The vector nature of SVG ensures that the line numbers remain crisp and clear at any zoom level.

    Cons

    Accessibility

    Ordered lists are more accessible to screen readers and assistive technologies, while SVG-based line numbers might be ignored or misinterpreted.

    Customization Complexity

    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.

    Browser Compatibility

    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.

    Dynamic Content Handling

    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.

  • Release Statement This article is reproduced at: https://dev.to/madsstoumann/line-numbers-for-using-svg-1216?1 If there is any infringement, please contact [email protected] to delete it
    Latest tutorial More>

    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