diff --git a/src/jqLite.js b/src/jqLite.js index e59805147886..f9f98f597c15 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -50,6 +50,7 @@ * - [`eq()`](http://api.jquery.com/eq/) * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name * - [`hasClass()`](http://api.jquery.com/hasClass/) + * - [`height()`](http://api.jquery.com/height/) - Limited to returning offsetHeight * - [`html()`](http://api.jquery.com/html/) * - [`next()`](http://api.jquery.com/next/) - Does not support selectors * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData @@ -69,6 +70,7 @@ * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers. * - [`unbind()`](http://api.jquery.com/off/) - Does not support namespaces * - [`val()`](http://api.jquery.com/val/) + * - [`width()`](http://api.jquery.com/width/) - Limited to returning offsetWidth * - [`wrap()`](http://api.jquery.com/wrap/) * * ## jQuery/jqLite Extras @@ -431,6 +433,49 @@ function getBooleanAttrName(element, name) { return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr; } +// Swap out CSS values in order to perform operation 'callback', +// and return the CSS values to their original state afterwards. +var swapCss = function (element, css, callback, args) { + var ret, prop, old = {}; + + for (prop in css) { + old[prop] = element.style[prop]; + element.style[prop] = css[prop]; + } + + ret = callback.apply(element, args || []); + + for (prop in css) { + element.style[prop] = old[prop]; + } + + return ret; +}; + +var swapDisplay = /^(none|table(?!-c[ea]).+)/; +var cssShow = { + position: 'absolute', + visibility: 'hidden', + display: 'block' +}; + +var jqLiteDimensions = function(name) { + var getter = function(element) { + return element[camelCase('offset-' + name)]; + }; + return function(element, value) { + var jq = jqLite(element); + if (isDefined(value)) { + return jq.css(name, value); + } else { + if (element.offsetWidth === 0 && swapDisplay.test(jq.css('display'))) { + return swapCss(element, cssShow, getter, [element]); + } + return getter(element); + } + }; +}; + forEach({ data: jqLiteData, inheritedData: jqLiteInheritedData, @@ -471,6 +516,11 @@ forEach({ if (val === '') val = 'auto'; } + // if getComputedStyle is available, leverage it. + if (window.getComputedStyle) { + val = val || window.getComputedStyle(element, null)[name]; + } + val = val || element.style[name]; if (msie <= 8) { @@ -565,7 +615,10 @@ forEach({ element.innerHTML = value; }, - empty: jqLiteEmpty + empty: jqLiteEmpty, + + width: jqLiteDimensions('width'), + height: jqLiteDimensions('height') }, function(fn, name){ /** * Properties: writes return selection, reads return first value diff --git a/test/jqLiteSpec.js b/test/jqLiteSpec.js index 931f9b2e5cb3..05912805c22b 100644 --- a/test/jqLiteSpec.js +++ b/test/jqLiteSpec.js @@ -1529,4 +1529,63 @@ describe('jqLite', function() { }); }); + describe('dimensions', function() { + beforeEach(function() { + a = jqLite(a); + b = jqLite(b); + c = jqLite(c); + forEach([a, b, c], function(elem, index) { + forEach(['height', 'width'], function(name) { + forEach(['', 'min-', 'max-'], function(prefix) { + elem.css(prefix+name, (index * 50) + 'px'); + }); + }); + // Element must be in body to test correctly. + jqLite(document).find('body').append(elem); + }); + }); + afterEach(function() { + forEach([a, b, c], function(elem) { + elem.remove(); + }); + }); + + describe('width', function() { + it('will return correct offsetWidth for hidden element', function() { + forEach([a, b, c], function(elem) { elem.css('display', 'none'); }); + expect(a.width()).toEqual(0); + expect(a[0].offsetWidth).toEqual(0); + expect(b.width()).toEqual(50); + expect(b[0].offsetWidth).toEqual(0); + expect(c.width()).toEqual(100); + expect(c[0].offsetWidth).toEqual(0); + }); + + it('will return correct offsetWidth for block element', function() { + forEach([a, b, c], function(elem) { elem.css('display', 'block'); }); + expect(a.width()).toEqual(0); + expect(b.width()).toEqual(50); + expect(c.width()).toEqual(100); + }); + }); + + describe('height', function() { + it('will return correct offsetHeight for hidden element', function() { + forEach([a, b, c], function(elem) { elem.css('display', 'none'); }); + expect(a.height()).toEqual(0); + expect(a[0].offsetHeight).toEqual(0); + expect(b.height()).toEqual(50); + expect(b[0].offsetHeight).toEqual(0); + expect(c.height()).toEqual(100); + expect(c[0].offsetHeight).toEqual(0); + }); + + it('will return correct offsetHeight for block element', function() { + forEach([a, b, c], function(elem) { elem.css('display', 'block'); }); + expect(a.height()).toEqual(0); + expect(b.height()).toEqual(50); + expect(c.height()).toEqual(100); + }); + }); + }); });