What is the most elegant way to check whether an element is in the normal flow using jQuery?
According to the CSS3 specification,
A box belongs t
I doubt theres a better way, but a different way would be:
1) Surround the element with a wrapper
2) Compare height and width of wrapper with wrapped element
For example:
$('#elementToTest').clone().addClass('clone').wrap('<div></div>')
if($('#elementToTest.clone').height()>$('#elementToTest.clone').parent().height()){
//outside the normal flow
}
I think another "in flow" requirement is that overflow
is set to visible
.
From the CSS2 spec:
Floats, absolutely positioned elements, block containers (such as inline-blocks, table-cells, and table-captions) that are not block boxes, and block boxes with 'overflow' other than 'visible' (except when that value has been propagated to the viewport) establish new block formatting contexts for their contents.
Based on the requirements you quoted and the overflow
requirement, this is one way to do it with jquery:
function isInFlow(elm, ctxRoot) {
ctxRoot = ctxRoot || document.body;
var $elm = $(elm),
ch = -1,
h;
if (!$elm.length) {
return false;
}
while ($elm[0] !== document.body) {
h = $elm.height();
if (h < ch || !okProps($elm)) {
return false;
}
ch = h;
$elm = $elm.parent();
if (!$elm.length) {
// not attached to the DOM
return false;
}
if ($elm[0] === ctxRoot) {
// encountered the ctxRoot and has been
// inflow the whole time
return true;
}
}
// should only get here if elm
// is not a child of ctxRoot
return false;
}
function okProps($elm) {
if ($elm.css('float') !== 'none'){
return false;
}
if ($elm.css('overflow') !== 'visible'){
return false;
}
switch ($elm.css('position')) {
case 'static':
case 'relative':
break;
default:
return false;
}
switch ($elm.css('display')) {
case 'block':
case 'list-item':
case 'table':
return true;
}
return false;
}
See this jsFiddle for test cases.
I'm not sure if it would be better to use window.getComputedStyle() or not.
The function is checking to see if elm
is in ctxRoot
's flow or block formatting context (as it was previously called, I think). If ctxRoot
is not supplied, it will check against the body
element. This does not check to make sure ctxRoot
is in flow. So, with this HTML
<div id="b" style="overflow: hidden;">
<div id="ba">ba
<p id="baa">baa</p>
<span id="bab">bab</span>
<span id="bac" style="display:block;">bac</span>
</div>
</div>
The test cases are:
var b = $('#b')[0];
console.log('no ',isInFlow(b));
console.log('no ',isInFlow('#ba'));
console.log('yes ',isInFlow('#ba', b));
console.log('no ',isInFlow('#baa'));
console.log('yes ',isInFlow('#baa', b));
console.log('no ',isInFlow('#bab'));
console.log('no ',isInFlow('#bab', b));
console.log('no ',isInFlow('#bac'));
console.log('yes ',isInFlow('#bac', b));
Instead of looking at it retroactively, you could pre-empt the need for this by using data annotations. Any time you create or define an element, set its attribute data-flow
to true or false.
For example:
var newDiv = document.createElement("div");
newDiv.style.position = "absolute";
newDiv.setAttribute("data-flow","false");
Or in html
<div style="position:absolute;" data-flow="false"></div>
And then you could simply select these elements with a selector:
$('*[data-flow=false]')