HTML 自适应高度的Js 算法
在网页布局中,想实现下面的效果是办不到的。
<div style="height:*"></div>
即,该 div 的高度填充父元素的剩余空间。 这时候,不得不使用 Js 来对该页面进行计算,得出剩余高度。
基础知识
CSS盒式模型,可以打开 FireBug 之类的工具看到,如下图:
注意数值, 从1 到16 是不同的。 宽高也是不同的,方便看出差异。
1. div的宽高是包含到 padding 的。 jquery.fn.height 是不包含 padding 的, 同样 dom.style.height 也是不包含 padding 的。
2. div 的 offsetTop , jQuery.fn.position().top , jQuery.fn.offsetTop 其顶点是包含到 border 的,即 offsetTop = div.borderTop上边缘线到页面的距离。
3. 原生的 js 没有提供 该元素相对父元素的位移。 getBoundingClientRect 方法获取的是该元素到页面的位移。 top = div.borderTop上边缘线到页面的距离。
算法
先画个场景,示意图:
var a = 容器;
var b = 在a容器内,上部元素,可能不只一个。
var c = 在a容器内,要填充高度的div ,肯定是一个元素 。
var d = 在a容器内,下部元素,可能不只一个。
结合盒式模型,其结构就会变复杂,因为有 margin,border,padding ,发挥大家的空间想像力吧。
以下公式采用简写,
MacroDefine
mt = martinTop , bt = borderTopWidth , pt = paddintTop ,
mb = martinBottom , bb = borderBottomWidth , pb = paddingBottom
由于b,d 不只一个,所以其高度不能通过元素遍历来求。
公式开始:
abJx = ab 间隙 abX = ab之间的getBoundingClientRect的差。 b.getBoundingClientRect - a.getBoundingClientRect abX = a.bt + a.pt + abJx + b.mt b.h = a.h - abJx - b.margin - b.border -b.padding => b.h = a.h - ( abX - a.bt - a.pt - b.mt ) - b.margin - b.border -b.padding => b.h = a.h - abX + a.bt + a.pt + b.mt - b.margin - b.border -b.padding => b.h = a.h - abX + a.bt + a.pt - b.mb - b.border -b.padding
求出的 c到底的高度 包含 d元素的高度, d元素的高 = d中第一个的上顶点 到 d中最后一个的上顶点的距离 + d最后一个的高度
这要求: 第一个元素是后续元素中海拔最高的元素,最后一个元素是后续元素中海拔最低的元素。
代码实现
代码从架构中摘出,基础函数从名字可以判断其含义, 代码没有判断元素的 height:* ,而是判断元素是否使用 FillHeight 属性。具体含义为:
FillHeight 在 $.ready 时设定高度
FillHeight- 在 window.onload 时设定高度
FillToBottom 填充到底,忽略d元素。很少情况下使用。
FillMinHeight 设置到 min-height 属性上
FillMaxHeight 设置到 max-height 属睡上。
如果没有 FillMinHeight 和 FillMaxHeight ,则设置到 height 属性上。
jv.GetFillHeightValue = jv.getFillHeightValue = function (con) { var jd = $(con); var pjd = jd.parent(), h; if (jd[0].tagName == "BODY") { //规定: HTML 下只有一个 BODY 且其 offsetTop , offsetLeft 均为0. // document.documentElement.clientHeight 是可见区域的大小 // document.documentElement.scrollHeight 是body内容的大小,包含:body 的 margin,border,padding if (document.documentElement.clientHeight > document.documentElement.scrollHeight) { //可见区域大,则使用 可见区域 - html.margin - html.border -html.padding - body.margin - body.border - body.padding h = document.documentElement.clientHeight; h = h -jv.GetInt(pjd.css("marginTop")) - jv.GetInt(pjd.css("marginBottom")) - jv.GetInt(pjd.css("borderTopWidth")) - jv.GetInt(pjd.css("borderBottomWidth")) - jv.GetInt(pjd.css("paddingTop")) - jv.GetInt(pjd.css("paddingBottom")) - jv.GetInt(jd.css("marginTop")) - jv.GetInt(jd.css("marginBottom")) - jv.GetInt(jd.css("borderTopWidth")) - jv.GetInt(jd.css("borderBottomWidth")) - jv.GetInt(jd.css("paddingTop")) - jv.GetInt(jd.css("paddingBottom")) ; } else { //有滚动条,则 //document.documentElement.scrollHeight = body的border大小 - body.border - body.padding h = document.documentElement.scrollHeight; h = h- jv.GetInt(jd.css("borderTopWidth")) - jv.GetInt(jd.css("borderBottomWidth")) - jv.GetInt(jd.css("paddingTop")) - jv.GetInt(jd.css("paddingBottom")) ; } } else if (jd[0].tagName == "HTML") { throw Error("不合法"); } else { //如果是 table tbody thead tfoot tr ,则使用只能有一个使用 FillHeight = TableContainer.Height - Table.Height if ($.inArray(jd[0].tagName, ["TBODY", "THEAD", "TFOOT", "TR", "TD", "TH"]) >= 0) { if (pjd.siblings().length === 0) { var ptab = pjd.closest("table"); h = ptab.parent().height() - ptab.height(); } h = Math.max(parseInt(jv.oriCss(pjd[0], "minHeight") || 0), parseInt(pjd.oriCss("height") || 0)); } else { h = pjd.height(); } /* a 是容器, b 是自适应高度的对象 h 是指 ContentHeight ,不包含 padding getBoundingClientRect 是border 的位置 公式: abJx = ab 间隙 abX = ab之间的getBoundingClientRect的差。 b.getBoundingClientRect - a.getBoundingClientRect abX = a.bt + a.pt + abJx + b.mt b.h = a.h - abJx - b.margin - b.border -b.padding => b.h = a.h - ( abX - a.bt - a.pt - b.mt ) - b.margin - b.border -b.padding => b.h = a.h - abX + a.bt + a.pt + b.mt - b.margin - b.border -b.padding => b.h = a.h - abX + a.bt + a.pt - b.mb - b.border -b.padding */ h = h - (jd[0].getBoundingClientRect().top - pjd[0].getBoundingClientRect().top) + jv.GetInt(pjd.css("borderTopWidth")) + jv.GetInt(pjd.css("paddingTop")) - jv.GetInt(jd.css("marginBottom")) - jv.GetInt(jd.css("borderBottomWidth")) - jv.GetInt(jd.css("borderTopWidth")) - jv.GetInt(jd.css("paddingTop")) - jv.GetInt(jd.css("paddingBottom")) ; } h += jv.GetInt(jd.attr("ofvh")); //再加上一个偏移量. OffsetValue return h; }; jv.fixHeight = jv.FixHeight = function (d) { //var selector = selector; //这句并不是废话, 不这样写的话. each 里面获取不到 selector 变量. var jd = $(d), theClass; theClass = (jd.hasClass("FillHeight") ? "FillHeight" : "FillHeight-"); if (d.tagName != "BODY") { var pjd = jd.parent(), ppjd; ppjd = pjd.closest(".FillHeight,.FillHeight-"); if (ppjd.length > 0) { if (jv.fixHeight(ppjd[0]) <= 0) return 0; } } var min = jd.hasClass("FillMinHeight"), max = jd.hasClass("FillMaxHeight"); var h = jv.GetFillHeightValue(jd); if (!jd.hasClass("FillToBottom") && !jd.hasClass("divSplit")) { /* 再减去 该元素的后续元素的整体高度. 前提: 第一个元素是后续元素中海拔最高的元素,最后一个元素是后续元素中海拔最低的元素 outerHeight = offsetHeight = 包含 border 的高度 如果后续的元素个数大于1个。 var d1 = 第一个弟弟 var last = 最后一个弟弟 var th? = FillHeight元素的弟弟总高度 th? = last.上顶点.top - d1.的上顶点.top + last.offsetHeight + d1.marginTop + last.marginBottom 如果后续的元素个数 == 1个。 var th? = d1.offsetHeight + d1.margin */ var findNode = function (n) { if (n.display == "none") return false; if (["SCRIPT", "STYLE", "META", "LINK"].indexOf(n.tagName) >= 0) return false; if (["absolute", "fixed"].indexOf(n.style.position) >= 0) return false; return true; } var next1 = jv.getNextNode(d, findNode); var next2 = jv.getNextNode(next1, findNode); //jv.getLastNode 如果 查找的元素 是最后一个,则返回 该元素。所以使用 next2 var last1 = jv.getLastNode(next2, findNode); if (next1 && last1) { h -= (last1.getBoundingClientRect().top - next1.getBoundingClientRect().top + last1.offsetHeight + jv.getInt(jv.css(next1, "marginTop")) + jv.getInt(jv.css(next1, "marginBottom"))); } else if (next1) { h -= (next1.offsetHeight + jv.getInt(jv.css(next1, "marginTop")) + jv.getInt(jv.css(next1, "marginBottom"))); } } if (h > 0) { if (!min && !max) { jd.css("height", h + "px"); } else if (min) { jd.css("minHeight", h + "px"); if ($.browser.msie) { jd.addClass("heightAuto").css("height", h + "px"); } } else if (max) { jd.css("maxHeight", h + "px"); } jd.addClass("FillHeighted").removeClass(theClass); } else { //debugger; //throw new Error("取FillHeight高度怎么是:" + h + " ?!"); } return h; };
作者:NewSea 出处:http://newsea.cnblogs.com/
QQ,MSN:iamnewsea@hotmail.com 如无特别标记说明,均为NewSea原创,版权私有,翻载必纠。欢迎交流,转载,但要在页面明显位置给出原文连接。谢谢。 |