在網頁布局中,想實現下面的效果是辦不到的。
<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; };