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…

visualstudio

TypeScript Support in Visual Studio 2013

There seems to be quite a lot of confusion surrounding the TypeScript support in Visual Studio 2013, and I believe that is partly down to how the tooling was previously implemented in WebEssentials.

As an early adopter of TypeScript in Visual Studio 2012 using Web Essentials, the development experience was pretty self explanatory.

  1. Add a new TypeScript file
  2. Edit (with nice preview window) and Save
  3. WebEssentials would generate a .js and .min.js file and automatically add them to the project

When moving to Visual Studio 2013 I was looking for the same functionality from the integrated plugin but found little success. However after much frustration I eventually figured out the differences between the tooling, and I now happen to think the plugin is actually better than the WebEssentials implementation (excluding the lack of preview window!).
Continue reading…

Durandal with ASP.NET MVC Conventions

Background

Durandal is a SPA framework that is built on top of already popular Javascript libraries including jQuery, Knockout and RequireJS.

It provides Javascript/HTML modularity, SPA lifecycle management, navigation and screen state management plus various other features that simplify SPA development.

However out of the box Durandal seems to throw away common ASP.NET MVC conventions in favour of its own.
Continue reading…