Chrome 50 and table-cell percentage heights

With the release of Chrome 50 there appears to be a fundamental breaking change in the rendering behaviour of content nested within table cells.

See this jsFiddle for an example.

Previously, Chrome (along with Safari, IE11 and Opera) would pass percentage heights implicitly to the child cell. So if height 100% was set on the table, the table cell would also be height 100%, making the div fill the full height of the table.

This was not the case with Firefox and versions of IE from 10 down. In fact, this behaviour has been reported as a bug with Mozilla since 1999, which seems to be acknowledged as such.

Chrome 50 appears to have joined this club, with complete disregard to the impact this change would have out in the wild. Based on my Chromium bug report, it seems we are not the only ones affected by this breaking change.

The justification for the change in this bug report states:

The new behavior is more spec compliant and, as mentioned above, there’s an easy workaround: add height:100% to the table cell.

Whilst in isolation the workaround is simple, dealing with a legacy web application consisting of thousands of pages now broken due to this change is a more serious problem.

Within this legacy application we apply a shim to “fix” this behaviour globally for Firefox and IE10. However there is a noticeable degrade in UI experience with doing this. Chrome has now been added to the list of browsers needing this shim.

The implementation may not be perfect, but hopefully it may help others who are dealing with the fallout of this high impact change.

(function ($, sr) {

    var debugEnabled = false;

    // debouncing function from John Hann
    // http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
    var debounce = function (func, threshold, execAsap) {
        var timeout;

        return function debounced() {
            var obj = this, args = arguments;
            function delayed() {
                if (!execAsap)
                    func.apply(obj, args);
                timeout = null;
            };

            if (timeout)
                clearTimeout(timeout);
            else if (execAsap)
                func.apply(obj, args);

            timeout = setTimeout(delayed, threshold || 100);
        };
    }

    // smartresize init 
    jQuery.fn[sr] = function (fn) { return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); };

    function isFullHeight(element) {
        return (element.css('height') === '100%' ||
                element.attr('height') === '100%' ||
                element.attr('isFullHeight') === 'true' ||
                element[0].style.height === '100%' ||
                (element[0].tagName === 'TD' && element[0].style.height == '' && !element.attr('height')));
    }

    function propagateCellHeights() {

		// **
		// ** Global fix solution that involves no markup changes 
		// **

		var tables = $('table');
		tables.each(function (i, table) {

			table = $(table);

			if ((table.css('height') === '100%' || table.attr('height') === '100%') && table.is(':visible')) {

				debug('Table (' + table.attr('id') + ') qualified.');

				var cells = table.find('> tbody > tr > td');
				var fullHeightCells = [];

				cells.each(function (j, cell) {
					cell = $(cell);
					if (isFullHeight(cell)) {
						debug('Cell (' + cell.attr('id') + ') qualified.');
						fullHeightCells.push(cell);
					}
				});

				$(fullHeightCells).each(function(j, cell) {
					cell.children().each(function(k, child) {
						child = $(child);
						if (isFullHeight(child)) {
							child.attr('isFullHeight', 'true');
							child.height(0);
						}
					});
				});

				$(fullHeightCells).each(function (j, cell) {
					var cellHeight = Math.floor(cell.height());
					cell.children().each(function (k, child) {
						child = $(child);
						if (isFullHeight(child)) {
							child.outerHeight(cellHeight);
						}
					});
				});
			}
		});
    }

    function debug(message) {
        if (debugEnabled) {
            console.log('[ie-tablecell-fix] : ' + message);
        }
    }

    $(window).load(function () {
        propagateCellHeights();
    });

    $(window).smartresize(function () {
        propagateCellHeights();
    });

})(window.jQuery, 'smartresize');

Note: This shim has been used in production for nearly two years without major issues, other than the stated degradation in UI experience.

browsers

Removal of showModalDialog() and the Enterprise

If you weren’t already aware, Google have made the decision to remove the showModalDialog() API from Chrome, which came into force with version 37. You can read the announcement for the move here:

Disabling showModalDialog

And a discussion on the issues here:

Intent to Remove: window.showModalDialog()

In addition to this, Mozilla have also indicated that they are to drop the API, so we better get used to it.
Continue reading…