0

I have a page with many divs that are laid out in a grid. Each div has text in it. I want the text to be just big enough to fill the div. I do this by increasing the text size until I detect that the div has scrolled, and then go back, as per this answer:

Auto-size dynamic text to fill div

This works really well on most browsers, including mobile ones, but on IE10 it is very slow. You can actually watch it making the text smaller. I am guessing that it is doing some kind of window-wide layout operation each time the font size changes.

Any idea how to suspend the redraw until all of the divs are done or otherwise improve performance?

Here is a simple fiddle showing the technique. Just imagine this with about 50 divs on the page. Instant in Chrome, takes several seconds in IE10:

(function($) {
$.fn.textfill = function(options) {
    var fontSize = options.maxFontPixels;
    var ourText = $('span:visible:first', this);
    var maxHeight = $(this).height();
    var maxWidth = $(this).width();
    var textHeight;
    var textWidth;
    do {
        ourText.css('font-size', fontSize);
        textHeight = ourText.height();
        textWidth = ourText.width();
        fontSize = fontSize - 1;
    } while ((textHeight > maxHeight || textWidth > maxWidth) && fontSize > 3);
    return this;
}
})(jQuery);

$(document).ready(function() {
    $('.jtextfill').textfill({ maxFontPixels: 200 });
});

jsfiddle: http://jsfiddle.net/pRJdY/

Community
  • 1
  • 1
Dave Vronay
  • 625
  • 7
  • 22

2 Answers2

0

well the jsFiddle fails cause there was a bug in the version of jQuery jsFiddle is referencing, http://bugs.jquery.com/ticket/13980.

So I created a local version of the sample. I think I see what you are talking about, but not totally sure. I see it go from the normal small text to much larger, but it happens real fast. I see the same behavior in Chrome. So my guess is till I have thousands of elements on the page I may not be able to recreate your issue.

I did optimize your JavaScript a little so I hope that helps you out a little:

(function ($) {

            $.fn.textfill = function (options) {

                var $this = $(this),
                    fontSize = options.maxFontPixels,
                    ourText = $('span:visible:first', this),
                    maxHeight = $this.height(),
                    maxWidth = $this.width(),
                    textHeight,
                    textWidth;

                do {
                    ourText.css('font-size', fontSize);
                    textHeight = ourText.height();
                    textWidth = ourText.width();
                    fontSize = fontSize - 1;
                } while ((textHeight > maxHeight || textWidth > maxWidth) && fontSize > 3);

                return this;

            }

        })(jQuery);
Chris Love
  • 3,740
  • 1
  • 19
  • 16
0

The problem is the huge number of DOM accesses. Fortunately, your algorithm can be greatly optimized.

Starting at 200px fontsize and going down, when you're going to end up around 10-30px is very slow. Better to go up, and better to do a binary search for the correct size.

Starting at the minimum size and going up is 5x faster, in this case, at least: http://jsfiddle.net/pRJdY/2/

Binary search cuts the time required in half, again: http://jsfiddle.net/pRJdY/4/

(function($) {
$.fn.textfill = function(options) {
    var start = (new Date).getTime(),
        node = $(this),
        max = options.maxFontPixels,
        min = 3,
        span = $('span:visible:first', this),
        maxHeight = node.height(),
        maxWidth = node.width(),
        size,
        isTooBig,
        finalSize = null;

    do {
        console.log(size);
        size = Math.round((min + max) / 2);
        span.css('font-size', size);
        isTooBig = (span.height() > maxHeight || span.width() > maxWidth);
        if (isTooBig) {
            max = size - 1;
        } else {
            min = size;
        }
        if (min === max) {
            finalSize = max;
        }
    } while (finalSize === null);

    span.css('font-size', finalSize);

    console.log('elements:', node.size(), 'elapsed time:', (new Date).getTime() - start);
    return this;
}
})(jQuery);
Patrick Fisher
  • 7,926
  • 5
  • 35
  • 28