Skip to content
This repository was archived by the owner on Jun 9, 2022. It is now read-only.

Commit 873092f

Browse files
Merge pull request #39 from georgecrawford/master
Small tweaks to margin collapsing
2 parents dd6fdd4 + 092681d commit 873092f

File tree

5 files changed

+166
-44
lines changed

5 files changed

+166
-44
lines changed

README.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ FTColumnflow supports the following browsers:
317317
* Mobile Safari (iOS 5+)
318318
* Android browser (ICS+)
319319
* Blackberry browser (PlaybookOS 2.0.1+)
320-
* Microsoft Internet Explorer (10+)
320+
* Microsoft Internet Explorer (9+ - IE9 needs a [classList polyfill](https://github.com/eligrey/classList.js))
321321

322322
## Testing
323323

@@ -343,6 +343,10 @@ FTColumnflow uses [Buster.js](http://busterjs.org/) as a TDD framework. This req
343343
$ buster test
344344

345345
There are a number of options for test output - see the [reporters documentation](http://busterjs.org/docs/test/reporters/) and the **Reporters** examples at the bottom of [the overview page](http://busterjs.org/docs/overview/).
346+
347+
* Alternatively, the tests can be run in PhantomJs, without the need for a Buster server or any captured browsers, using the simple command:
348+
349+
$ grunt test
346350

347351
## Credits and collaboration
348352

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ftcolumnflow",
3-
"version": "0.3.0",
3+
"version": "0.3.3",
44
"author": "FT Labs <[email protected]> (http://labs.ft.com/)",
55
"description": "FTColumnflow is a polyfill that fixes the inadequacies of CSS column layouts.",
66
"contributors": [

src/FTColumnflow.js

+42-30
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,13 @@ var FTColumnflow = (function () {
7474
},
7575

7676
// CSS Style declarations
77-
cssStyles = '#[targetId] { position: relative; height: 100%; }\n'
78-
+ '#[targetId] .[preloadAreaClassName].[pageClass] { visibility: hidden; position: absolute; overflow: hidden; }\n'
79-
+ '#[targetId] .[preloadFixedAreaClassName] { visibility: hidden; position: absolute; }\n'
80-
+ '#[targetId] .[pageClass] { position: absolute; width: [viewportWidth]px; height: [viewportHeight]px; [pageArrangement] }\n'
81-
+ '#[targetId] .[columnClass] { position: absolute; width: [columnWidth]px; overflow: hidden; }\n'
82-
+ '#[targetId] .[pageClass] .[fixedElementClassName] { position: absolute; }\n'
83-
+ '#[targetId] .[pageClass] .[columnClass] > :first-child { margin-top: 0px; }\n',
77+
cssStyles = '#[targetId] { position: relative; height: 100%; }\n' +
78+
'#[targetId] .[preloadAreaClassName].[pageClass] { visibility: hidden; position: absolute; overflow: hidden; }\n' +
79+
'#[targetId] .[preloadFixedAreaClassName] { visibility: hidden; position: absolute; }\n' +
80+
'#[targetId] .[pageClass] { position: absolute; width: [viewportWidth]px; height: [viewportHeight]px; [pageArrangement] }\n' +
81+
'#[targetId] .[columnClass] { position: absolute; width: [columnWidth]px; overflow: hidden; }\n' +
82+
'#[targetId] .[pageClass] .[fixedElementClassName] { position: absolute; }\n' +
83+
'#[targetId] .[pageClass] .[columnClass] > :first-child { margin-top: 0px; }\n',
8484

8585
cssColumnStyles = '#[targetId] .[columnClass].[columnClass]-[columnNum] { left: [leftPos]px; }\n',
8686

@@ -124,7 +124,6 @@ var FTColumnflow = (function () {
124124
fixedContent,
125125

126126
// Dimensions
127-
maxColumnHeight,
128127
colDefaultBottom,
129128
colMiddle,
130129
minFixedPadding,
@@ -361,6 +360,8 @@ var FTColumnflow = (function () {
361360
config.layoutDimensions.columnWidth = ((config.layoutDimensions.pageInnerWidth + config.layoutDimensions.columnGap) / config.layoutDimensions.columnCount) - config.layoutDimensions.columnGap;
362361
}
363362
}
363+
364+
config.layoutDimensions.columnHeight = config.lineHeight ? _roundDownToGrid(config.layoutDimensions.pageInnerHeight) : config.layoutDimensions.pageInnerHeight;
364365
}
365366

366367

@@ -522,6 +523,9 @@ var FTColumnflow = (function () {
522523
config.lineHeight = _mode(lineHeights);
523524
}
524525

526+
// Now the line-height is known, the column height can be determined
527+
config.layoutDimensions.columnHeight = config.lineHeight ? _roundDownToGrid(config.layoutDimensions.pageInnerHeight) : config.layoutDimensions.pageInnerHeight;
528+
525529
// For debugging, show the grid lines with CSS
526530
if (showGrid) {
527531

@@ -696,7 +700,7 @@ var FTColumnflow = (function () {
696700
bottomSplitPoint = _roundUpToGrid(elementTopPos + normalisedElementHeight, true);
697701

698702
if (topSplitPoint < 0) topSplitPoint = 0;
699-
if (bottomSplitPoint > maxColumnHeight) bottomSplitPoint = maxColumnHeight;
703+
if (bottomSplitPoint > config.layoutDimensions.columnHeight) bottomSplitPoint = config.layoutDimensions.columnHeight;
700704
break;
701705

702706
case 'bottom':
@@ -835,9 +839,8 @@ var FTColumnflow = (function () {
835839
totalColumnHeight = 0;
836840

837841
// Set the maximum column height to a multiple of the lineHeight
838-
maxColumnHeight = config.lineHeight ? _roundDownToGrid(config.layoutDimensions.pageInnerHeight) : config.layoutDimensions.pageInnerHeight;
839-
colDefaultBottom = maxColumnHeight + config.layoutDimensions.colDefaultTop;
840-
colMiddle = config.layoutDimensions.colDefaultTop + (maxColumnHeight / 2);
842+
colDefaultBottom = config.layoutDimensions.columnHeight + config.layoutDimensions.colDefaultTop;
843+
colMiddle = config.layoutDimensions.colDefaultTop + (config.layoutDimensions.columnHeight / 2);
841844
minFixedPadding = config.minFixedPadding * config.lineHeight;
842845
fixedPadding = _roundUpToGrid(minFixedPadding);
843846

@@ -897,36 +900,45 @@ var FTColumnflow = (function () {
897900

898901
function _addFlowedElement(element, index) {
899902

900-
var originalMargin, existingMargin, totalElementHeight,
901-
desiredElementHeight, newMargin, overflow, loopCount,
903+
var originalMargin, existingMargin, nextElementOffset, elementHeight,
904+
newMargin, largestMargin, overflow, loopCount,
902905

903906
nextElement = element.nextSibling;
904907

905908
// Check if it's necessary to sanitize elements to conform to the baseline grid
906909
if (config.standardiseLineHeight) {
907910

908-
originalMargin = parseFloat(element.getAttribute('data-cf-original-margin'), 10) || null;
909911
existingMargin = parseFloat(window.getComputedStyle(element).getPropertyValue('margin-bottom'), 10);
910912

911-
if (null === originalMargin) {
912-
originalMargin = existingMargin;
913-
element.setAttribute('data-cf-original-margin', originalMargin);
913+
// If reflowing is enabled, try to read the original margin for the
914+
// element, in case it was already modified
915+
if (config.allowReflow) {
916+
originalMargin = parseFloat(element.getAttribute('data-cf-original-margin'), 10) || null;
917+
if (null === originalMargin) {
918+
originalMargin = existingMargin;
919+
element.setAttribute('data-cf-original-margin', originalMargin);
920+
} else if (originalMargin !== existingMargin) {
921+
922+
// Return the element to its original margin
923+
element.style.marginBottom = originalMargin + 'px';
924+
}
914925
} else {
915-
existingMargin = originalMargin;
926+
originalMargin = existingMargin;
916927
}
917928

918-
// Return the element to its original margin
919-
if (originalMargin !== existingMargin) {
920-
element.style.marginBottom = originalMargin + 'px';
921-
}
929+
nextElementOffset = _getNextElementOffset(element, nextElement);
930+
elementHeight = element.offsetHeight;
922931

923-
totalElementHeight = _getElementHeight(element, nextElement);
924-
desiredElementHeight = _roundUpToGrid(totalElementHeight);
932+
// The next element's top is not aligned to the grid
933+
if (nextElementOffset % config.lineHeight) {
925934

926-
newMargin = desiredElementHeight - totalElementHeight + existingMargin;
935+
// Allow for collapsing margins
936+
largestMargin = Math.max(existingMargin, nextElement ? parseFloat(window.getComputedStyle(nextElement).getPropertyValue('margin-top'), 10) : 0);
937+
newMargin = _roundUpToGrid(elementHeight) - elementHeight + _roundUpToGrid(largestMargin);
927938

928-
if (newMargin !== existingMargin) {
929-
element.style.marginBottom = newMargin + 'px';
939+
if (newMargin !== existingMargin) {
940+
element.style.marginBottom = newMargin + 'px';
941+
}
930942
}
931943
}
932944

@@ -944,7 +956,7 @@ var FTColumnflow = (function () {
944956
if (loopCount >= 30) console.error('FTColumnflow: Caught and destroyed a loop when wrapping columns for element', element.outerHTML.substr(0, 200) + '...');
945957
}
946958

947-
function _getElementHeight(element, nextElement) {
959+
function _getNextElementOffset(element, nextElement) {
948960
if (!element.getBoundingClientRect) {
949961
return nextElement ? (nextElement.offsetTop - element.offsetTop) : element.offsetHeight;
950962
}
@@ -1267,7 +1279,7 @@ var FTColumnflow = (function () {
12671279
return {
12681280
elements: [],
12691281
overflow: 0,
1270-
height: maxColumnHeight,
1282+
height: config.layoutDimensions.columnHeight,
12711283
top: config.layoutDimensions.colDefaultTop,
12721284
bottom: colDefaultBottom
12731285
};

test/baselinegrid-test.js

+86-12
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,6 @@ buster.testCase('BaselineGrid', {
3838
assert.match(column.offsetHeight, 595);
3939
},
4040

41-
'ShouldReduceColumnHeightToNearestMultipleOfLineheight' : function() {
42-
43-
document.body.className = 'lineheight17';
44-
45-
createCf().flow('<p>flowedContent</p>');
46-
47-
var column = target.querySelector('.cf-column-1');
48-
49-
assert.match(column.offsetHeight, 595);
50-
},
51-
5241
'ShouldCalculateCorrectGridHeightWhenLineheightIsInEms' : function() {
5342

5443
document.body.className = 'lineheight-in-ems';
@@ -182,7 +171,7 @@ buster.testCase('BaselineGrid', {
182171
assert.match(parags[2].offsetTop, 146);
183172
},
184173

185-
'ShouldRoundUpElementMarginsWhenConfigured' : function() {
174+
'ShouldRoundUpAndCollapseElementMarginsWhenConfigured' : function() {
186175

187176
document.body.className = 'unpadded-parags';
188177

@@ -193,12 +182,97 @@ buster.testCase('BaselineGrid', {
193182
var column = target.querySelector('.cf-column-1');
194183
var parags = column.getElementsByTagName('p');
195184

185+
// 53-px parags with a collapsed 20px top/bottom margin, so rounded-up to 80px
186+
assert.match(parags[0].offsetTop, 0);
187+
assert.match(parags[1].offsetTop, 80);
188+
assert.match(parags[2].offsetTop, 160);
189+
},
190+
191+
'ShouldRoundUpTopMargin' : function() {
192+
193+
document.body.className = 'unpadded-parags top-margin';
194+
195+
createCf({
196+
standardiseLineHeight : true,
197+
}).flow('<p>Test paragraph</p><p>Test paragraph</p><p>Test paragraph</p>');
198+
199+
var column = target.querySelector('.cf-column-1');
200+
var parags = column.getElementsByTagName('p');
201+
202+
// 53-px parags with a 20px margin, so rounded-up to 80px
203+
assert.match(parags[0].offsetTop, 0);
204+
assert.match(parags[1].offsetTop, 80);
205+
assert.match(parags[2].offsetTop, 160);
206+
},
207+
208+
'ShouldRoundUpBottomMargin' : function() {
209+
210+
document.body.className = 'unpadded-parags bottom-margin';
211+
212+
createCf({
213+
standardiseLineHeight : true,
214+
}).flow('<p>Test paragraph</p><p>Test paragraph</p><p>Test paragraph</p>');
215+
216+
var column = target.querySelector('.cf-column-1');
217+
var parags = column.getElementsByTagName('p');
218+
196219
// 53-px parags with a 20px margin, so rounded-up to 80px
197220
assert.match(parags[0].offsetTop, 0);
198221
assert.match(parags[1].offsetTop, 80);
199222
assert.match(parags[2].offsetTop, 160);
200223
},
201224

225+
'1pxMarginShouldRoundUpTo1GridLine' : function() {
226+
227+
document.body.className = 'margin1px';
228+
229+
createCf({
230+
standardiseLineHeight : true,
231+
}).flow('<p>Test paragraph</p><p>Test paragraph</p><p>Test paragraph</p>');
232+
233+
var column = target.querySelector('.cf-column-1');
234+
var parags = column.getElementsByTagName('p');
235+
236+
// 60-px parags with a 1px top margin (except for first), so rounded-up to 80px
237+
assert.match(parags[0].offsetTop, 0);
238+
assert.match(parags[1].offsetTop, 80);
239+
assert.match(parags[2].offsetTop, 160);
240+
},
241+
242+
'21pxMarginShouldRoundUpTo2GridLines' : function() {
243+
244+
document.body.className = 'margin21px';
245+
246+
createCf({
247+
standardiseLineHeight : true,
248+
}).flow('<p>Test paragraph</p><p>Test paragraph</p><p>Test paragraph</p>');
249+
250+
var column = target.querySelector('.cf-column-1');
251+
var parags = column.getElementsByTagName('p');
252+
253+
// 60-px parags with a 1px top margin (except for first), so rounded-up to 80px
254+
assert.match(parags[0].offsetTop, 0);
255+
assert.match(parags[1].offsetTop, 100);
256+
assert.match(parags[2].offsetTop, 200);
257+
},
258+
259+
'1pxMarginOnUnevenHeightShouldRoundUpTo1GridLine' : function() {
260+
261+
document.body.className = 'unevenmargin1px';
262+
263+
createCf({
264+
standardiseLineHeight : true,
265+
}).flow('<p>Test paragraph</p><p>Test paragraph</p><p>Test paragraph</p>');
266+
267+
var column = target.querySelector('.cf-column-1');
268+
var parags = column.getElementsByTagName('p');
269+
270+
// 53-px parags with a 1px top margin (except for first), so rounded-up to 80px
271+
assert.match(parags[0].offsetTop, 0);
272+
assert.match(parags[1].offsetTop, 80);
273+
assert.match(parags[2].offsetTop, 160);
274+
},
275+
202276
'StandardisedLineHeightShouldNotAffectElementHeight' : function() {
203277

204278
document.body.className = 'unpadded-parags';

test/css/BaselineGridTest.css

+32
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,35 @@ img { width: 100%; height: auto; }
8585
.unequal-margin p {
8686
margin: 40px 0 20px;
8787
}
88+
89+
/* Paragraphs have notional 1px margin which should be rounded up to a grid line */
90+
.margin1px p {
91+
margin: 1px 0 0;
92+
height: 60px;
93+
}
94+
95+
.margin1px p:first-of-type {
96+
margin-top: 0;
97+
}
98+
99+
/* Paragraphs have 21px margin which should be rounded up to two grid lines */
100+
.margin21px p {
101+
margin: 21px 0 0;
102+
height: 60px;
103+
}
104+
105+
.margin21px p:first-of-type {
106+
margin-top: 0;
107+
}
108+
109+
/* Paragraphs are 7px short of the grid, and have notional 1px margin which should therefore
110+
be rounded up to a grid line*/
111+
.unevenmargin1px p {
112+
margin: 1px 0 0;
113+
height: 53px;
114+
}
115+
116+
.unevenmargin1px p:first-of-type {
117+
margin-top: 0;
118+
}
119+

0 commit comments

Comments
 (0)