Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / vue.js

Dynamically Resize Text to Fit in a div

2.39/5 (8 votes)
25 Jun 2018CPOL2 min read 33.3K   163  
WPF Viewbox style resizing of dynamic content to fit within the borders of a div.
In this article, you will see a demo of how to dynamically resize text contents of a div to make it fit within its boundaries.

Introduction

This is a simple demo of how to dynamically resize text contents of a div so they fit within its boundaries. It uses HTML/Javascript/CSS, and optionally uses Vue for binding.

Background

We are migrating a line-of-business app from UWP to HTML5. In UWP and WPF, there is a control called the Viewbox which very conveniently resizes its contents to fit. Due to the large amount of variably-sized information displayed in this app, it uses Viewboxes extensively. We couldn't replicate it in HTML5 without finding a way to replicate Viewbox functionality for text.

We found a lot of examples on resizing images to fit a div and on resizing divs so that their content fits, but we could not find an example of dynamically resizing text to fit inside a given div. Here's our approach to the problem.

EDIT: A commenter suggested using a CSS rule of 'font-size: .5vw'. This works great for static content or content that is dynamic server-side. In our case, we're updating the DOM directly with JavaScript. That CSS rule doesn't change the text size in response to dynamic client-side changes to the content.

Using the Code

For simplicity, we consolidated the solution into a single HTML file. Because we're using Vue in this project, one of the demos also shows how to use this approach with that framework. It should be pretty easy to adapt to any popular framework.

HTML
<html>
<head>
<style>
.resizable {
    border: 1px gray solid;
    width: 50%;
    height: 10%;
    background-color: aliceblue;
    text-align: center;
    align-items: center;
    justify-content: center;
    display: flex;
    font-family: sans-serif;
}
</style>
</head>

<body onresize="resizeContents()" onload="resizeContents()">
<h1>Dynamic Font Resize Demos</h1>

<h2>Demo 1: Set the content then resize it</h2>

Input:

<input id='demoInput1' onkeyup="demo1()" />
<br /> Output:
<div id='demoDiv1' class='resizable' style='height: 10%;'>

Please Enter Input
</div>

<h2>Demo 2: Resizing with Vue</h2>

Input:
<input id='demoInput2' onkeyup="demo2()" />
<br /> Output:
<div id='demoDiv2' class='resizable'>
{{ content }}
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
function demo1() {
    var div = document.getElementById('demoDiv1');
    var input = document.getElementById('demoInput1');
    
    div.innerText = input.value;

    resizeContents();
}

var demoVue2 = new Vue({
    el: '#demoDiv2',
    data: {
        content: "Please Enter Input"
    },
    updated: function () { resizeContents(); }
})

function demo2() {
    var input = document.getElementById('demoInput2');

    demoVue2.content = input.value;
}

function resizeContents() {
    var resizableDivs = document.getElementsByClassName('resizable');
    for (index = 0; index < resizableDivs.length; ++index) {
        var div = resizableDivs[index];
        if (div.innerText != '') {
            var divWidth = div.clientWidth;
            var divHeight = div.clientHeight;

            var contentWidth = div.scrollWidth;
            var contentHeight = div.scrollHeight;

            var fontSize = div.style.fontSize;
            fontSize = Number(fontSize.substring(0, fontSize.length - 2));

            while (contentWidth <= divWidth && contentHeight <= divHeight) {
                fontSize += 1;
                div.style.fontSize = fontSize + 'px';
                contentWidth = div.scrollWidth;
                contentHeight = div.scrollHeight;
            }

             while (contentWidth > divWidth || contentHeight > divHeight) {
                 fontSize -= 1;
                 div.style.fontSize = fontSize + 'px';

                 contentWidth = div.scrollWidth;
                 contentHeight = div.scrollHeight;
             }
        }
    }
}

</script>
</body>
</html

Points of Interest

This approach is superior to Viewbox in at least one respect: unlike Viewbox, the browser automatically takes care of text wrapping. You can resize the window, add or remove text, etc. and the text wrapping will ensure that the div is constantly filled to capacity.

We've only found a couple of flaws with this solution:

  • The character 'f' kerns such that it can intersect with the border of the div. Meh.
  • The vertical centering relies on CSS Flexbox which isn't supported in older browsers.

Thanks to the whole community for the help over the years! Hopefully, this article pays that forward in some small way.

Happy coding!

History

  • 16th June, 2018: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)