zoukankan      html  css  js  c++  java
  • 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原创,版权私有,翻载必纠。欢迎交流,转载,但要在页面明显位置给出原文连接。谢谢。
  • 相关阅读:
    oracle 游标的使用
    mvc的表单发送ajax请求,太强大了!!!!
    报表页面的异步加载
    一道关于集合分组并进行笛卡尔积的题目思路
    EF常用操作截图
    大数乘法取模运算(二进制)
    求sqrt()底层效率问题(二分/牛顿迭代)
    CodeForces 282C(位运算)
    Codeforces Round #371 (Div. 2)(setunique)
    Codeforces Round #370 (Div. 2)(简单逻辑,比较水)
  • 原文地址:https://www.cnblogs.com/newsea/p/3072589.html
Copyright © 2011-2022 走看看