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;
    };
alarm   作者:NewSea     出处:http://newsea.cnblogs.com/    QQ,MSN:iamnewsea@hotmail.com

  如无特别标记说明,均为NewSea原创,版权私有,翻载必纠。欢迎交流,转载,但要在页面明显位置给出原文连接。谢谢。
原文地址:https://www.cnblogs.com/newsea/p/3072589.html