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;
    };

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM