Should I use Cheerio or the actual DOM



  • I'm injecting some HTML into my reports. Sometimes the injected elements are wider than my page, this mostly happens with images and tables.

    I need to scale down these elements, otherwise the whole canvas will grow larger and the rest of the report will be "zoomed out", i.e. will be rendered smaller than it should.

    I can do this successfully with an inline script using the DOM in Chrome, but I would rather have the resize functionality in a helper so I can reuse it easier in many reports.

    Would it make sense to use Cheerio for this? I'm not a fan of introducing more dependencies, so I'd like to hear pros and cons first.

    Alternatives:

    1. Manipulate the elements directly in the DOM via an inline script.
    2. Manipulate the elements with Cheerio from a helper function
    3. Maybe I could put the inline script in a component and just add the component to the reports where it is needed, i.e. reuse the inline script via a component?

    Here is the inline script I'm currently using:

       <script>
        function scaleElementToFit() {
          const targetWidth = 670; // Available width between page margins of an A4 page in pixels
          const elements = document.querySelectorAll('table');
    
          elements.forEach(function(element) {
            const actualWidth = element.offsetWidth;
            if (actualWidth > targetWidth) {
               const scaleFactor = targetWidth / actualWidth;
              element.style.transform = 'scale(' + scaleFactor + ')';
             element.style.transformOrigin = 'left top';
            }
          });
        }
    
        window.onload = scaleElementToFit;
        </script>
    

  • administrators

    hi @msageryd

    to me your 3 option sounds better to me, i don't understand how you 2 option will work, since the helper does not have access to DOM only js on server side i don't know how you replicate the same, because in your logic you are reading the calculated dimensions rendered by the browser, you don't have that on the server



  • The component in which the html content is injected looks like this:

    <div>
        {{{content}}}
    </div>
    

    I haven't used cheerio, but I assume a helper function could look something like this (with help from ChatGPT)

    function scaleElementToFit(content, maxWidth) {
        var cheerio = require('cheerio');
        const $ = cheerio.load(content);
    
        $('table').each(function() {
            var table = $(this);
            var actualWidth = table.outerWidth(true);
            if (actualWidth > maxWidth) {
                var scaleFactor = maxWidth / actualWidth;
                table.css({
                    'transform': 'scale(' + scaleFactor + ')',
                    'transform-origin': 'top left',
                    'max-width': maxWidth + 'px',
                    'width': '100%'
                });
            }
        });
    
        return $.html(); // Returns the modified HTML
    }
    

    If I could get the helper concept working I could do the scaling more dynamic and also take into account different indentation levels, i.e. different max available width for different divs.

    Maybe like this:

    <div>
        {{{scaleElementToFit content 650}}}
    </div>
    


  • I went with option 3, i.e. made a component from the resize script. If anyone else needs to solve this, I'll leave the code here.

    The component:

        <script>
            function scaleElementToFit() {
            const elements = document.querySelectorAll('table');
    
            elements.forEach(function(element) {
                const targetWidth = element.parentElement.clientWidth; // Get the width of the parent element      
                const actualWidth = element.offsetWidth;
                if (actualWidth > targetWidth) {
                const scaleFactor = targetWidth / actualWidth;
                element.style.transform = 'scale(' + scaleFactor + ')';
                element.style.transformOrigin = 'left top';
                }
            });
            }
    
            window.onload = scaleElementToFit;
        </script>
    

    Calling the component from the main report to ensure that no tables are too wide.

    {{{component "resize_tables"}}}
    

    N.B. the same issue can happen with images, but images are easier to resize since you don't have to take into account wordbreaks. This is my css to make the same adjustments to images.

    I had to use 99% instead of 100%. I don't know exactly why. If I use 100% the image will be a tad too large and the rest of the report will be scaled down.

    div img {
       max-width: 99%;
       height: auto;
    }
    

  • administrators

    Great that you made it work with components!

    The thing about cheerio is that by design they dont have access to calculated dimensions like outer width, but long time since i checked its api so i could be wrong


Log in to reply
 

Looks like your connection to jsreport forum was lost, please wait while we try to reconnect.