获取组件的实际宽度和高度

Posted

技术标签:

【中文标题】获取组件的实际宽度和高度【英文标题】:Get actual width and height of a component 【发布时间】:2013-09-24 08:22:00 【问题描述】:

我们在 javascript 中面临一个相当可怕的问题,我们似乎都没有能力解决:

我们如何在页面上不实际显示组件的情况下获取 DOM 元素的宽度和高度,包括子元素、整个盒子模型等?

记住:我正在寻找建议。即使是不能完全回答问题(或不完全符合指定参数)的答案也可能而且可能会有帮助。

主要目标:我通过 Javascript 将 html 元素添加到页面中 - 来自数据库的具有大小和样式的 HTML 元素。问题是它们行为不端,通常是错误的对齐方式,由于填充/边距,一个元素大于另一个元素,因此我需要检查它们的实际大小以解决这些问题。

最终的应用程序将是一个,正如 BigMacAttack 在 cmets 中所描述的那样,一个“紧密结合的第 3 方 HTML 控件的马赛克”将非常准确。它需要看起来很像成熟的桌面应用程序,而 HTML 似乎对这个想法充满热情。不是我责备它。

不管怎样,这里有一些示例代码:

JavaScript:

function exampleElement(caption, content) 
    this.caption = caption;
    this.content = content;
    this.rootElement = document.createElement("div");


exampleElement.prototype.constructElement = function() 
    var otherElement = document.createElement("p");
    this.rootElement.className = "exampleElement";
    this.rootElement.textContent = this.caption; 
    otherElement.className = "exampleP";
    otherElement.textContent = this.content;
    this.rootElement.appendChild(otherElement);
    /*I need to know size of the otherElement here*/
    /*here goes code adding stuff into rootElement*/
;

window.onload = function() 
    var ex = new exampleElement("Hello", "Here's text");
    ex.constructElement();
    document.body.appendChild(ex.rootElement);
;

CSS:

.exampleElement 
    padding: 5px;
    margin: 6px;


.exampleElement .exampleP 
    padding: 20px;
    margin: 6px;

A fiddle

现在,我们需要我们的页面对窗口大小和各个组件的内容做出动态反应,这就是为什么在显示对象之前能够获取对象的大小很重要。同样重要的是,对象的创建明确分为三个阶段:

通过新创建

DOM树的构造(constructElement)

添加到文档中(直接添加到正文中或添加到另一个 DOM 树中)

在构建阶段了解各个元素的大小非常重要。

到目前为止,我们已经尝试通过 jQuery、DOM 宽度和高度属性来测量它,但这些都不适用于不直接显示在页面上的 DOM 对象。我尝试的另一种方法是将对象添加到 document.body 中的几个函数,获取宽度和高度,然后立即将其删除 - 但是,由于我们的 CSS 文件非常具体,除非您插入整个 rootElement,否则这是不可靠的,这将是由于我们的组件变得相当复杂,因此性能和内存都非常糟糕。

我认为完全删除 .CSS 文件并直接通过 JS 定义样式的方法至少可以解决我们的部分困境,但必须有更好的方法。

开始赏金以获得更多想法和建议。只是射击人,即使答案不完全在问题的范围内(你会/你是怎么做的等等) - 我试图实现的目标是让我的 JS 生成的 HTML 控件正确地组合在一起。

【问题讨论】:

将对象插入隐藏的iframe 工作吗? @Mathijs Flietstra 好吧,我不妨将它直接插入 body 并获取其子项的大小,问题是它在性能方面变得太成问题(要么没有应用,要么没有应用样式,任君挑选) 你不能有position:absolute; opacity:0;,然后在你有尺寸时删除它吗? 你不能。只需将其添加到身体,测量它,然后将其移除。如果你在同一个代码块中这样做,它甚至不会在屏幕上闪烁,而且速度非常快(如果这成为一个瓶颈,你需要重新考虑你是如何做事的;计算 CSS 将花费比附加到 DOM 结构) 我过去曾尝试过类似的事情,但从未构建过如此复杂的东西,以至于将其添加到文档中会有很大的危害。当我陷入 Javascript 中的这些宽度问题时,我通常将它们添加到文档中或使用固定大小... 【参考方案1】:

如果您最初需要隐藏诸如手风琴、滑块和其他需要边界框信息才能正常工作的组件,您经常会遇到这种情况。

一个简单的技巧是添加 CSS 来隐藏相关内容的可见性并确保它不会闪烁或干扰您的布局。

它可以很简单:

   visibility:hidden;
    position:absolute;
    left: -9999px;

然后在您准备好显示组件时将位置设置回静态或相对,并将可见性设置回可见。

小提琴就在这里,但没什么大不了的:http://jsfiddle.net/gwwar/ZqQtz/1/

【讨论】:

这也是我要做的第一件事,但是我们永远无法确定最终会在页面上显示什么 - 要显示的元素、它们的大小、位置和样式从 DB 中获取,甚至是基于用户输入的。为未来的 Google 员工 +1,感谢您的努力。 嗯,如果你正在做一些相当复杂的事情,并且想将其中的一些工作卸载到服务器上,听起来你几乎想使用无头浏览器。您需要为组件创建特殊页面,这会非常麻烦,但如果您的机器上安装了正确的字体,计算会非常准确。使用 PhantomJS 和 CasperJS 编写脚本非常简单。无论如何,我可能会考虑为测试集成这样做,但我不建议将其用于产品。 另一个要求是应用程序必须在某些限制下离线运行。但是在服务器端进行计算并不是一个糟糕的主意,毕竟我们确实可以访问 JS 和 CSS 文件,而且我们必须在某个时候将它们发送给客户端。仍然会很糟糕。我的大脑必须处理它,谢谢 我能想到的唯一其他可能感兴趣的就是shadow dom 这是实验性的,仅在 chrome 上受支持。我自己还没有玩过这个,但你可能想阅读一下。 嗯...现在我知道了另一个非常酷的功能,由于浏览器的限制,我将无法使用它。我需要问问别人为什么我们一年后没有开始开发。【参考方案2】:

使用 javascript 获取渲染的框模型 DOM 节点的宽度和高度而不实际将其添加到要显示的 DOM 是不可能的。

为了证明这一点,让我们来看看如何在浏览器内部计算 DOM 节点的渲染高度和宽度。我将参考 WebKit 如何处理这个问题,因为它是最常用的布局引擎。

随着文档被解析,DOM 节点被添加到“DOM 树”中,Renderers 被创建用于需要显示的 DOM 节点。这就是“渲染树”的构建方式。

以下是 Dave Hyatt 在官方 WebKit 博客上题为 "WebCore Rendering I – The Basics" 的文章的节选:

“渲染器是通过 DOM 上称为附件的过程创建的。 在解析文档并添加 DOM 节点时,调用了一个方法 attach 在 DOM 节点上被调用以创建渲染器。

void attach()

attach 方法计算 DOM 节点的样式信息。如果 元素的显示 CSS 属性设置为 none 或者如果节点是 具有 display: none 设置的元素的后代,然后没有渲染器 将被创建。”

因此,为了提高效率,浏览器甚至不会为 display 设置为 none 的元素计算样式信息。因此,无法通过 javascript 访问该信息。但是,如果 display 属性 not 设置为 none,则会发生以下情况:

"在附件期间,DOM 查询 CSS 以获取样式信息 一个元素。结果信息存储在一个称为 a 的对象中 RenderStyle... 可以从 RenderObject 访问 RenderStyle 使用 style() 方法...主要的主力子类之一 RenderObject 是 RenderBox。这个子类表示对象 遵守 CSS 盒子模型。这些包括任何有边界的对象, 内边距、边距、宽度和高度。”

因此,如果您的用例允许您直接从浏览器通过 C/C++ 检索框模型渲染高度和宽度,并通过其他方式将其传递给您的 JavaScript 代码,那么您可以查询高度/宽度方法每个 DOM 元素的 RenderBox 子类。这基本上就是 WebKit 开发者工具获取这些信息的方式。

【讨论】:

感谢您为回答所做的努力。我实际上知道为什么它不能完成——无论如何,我需要以某种方式完成它。 C/C++ 路线会很有趣,但对于已发布的应用程序来说并不是那么有用。我想显示元素,获取它们的大小并隐藏它们是唯一的方法。 @Fenixp “我想显示元素,获取它们的大小并隐藏它们是唯一的方法。” - 正确。如果您希望获得实际呈现的大小(即不使用 jquery.actual 等库进行猜测),那么这是唯一的方法。请务必查看this SO question 及其对一些实施想法的回答。【参考方案3】:

你可以试试这个 jQuery 插件:https://github.com/dreamerslab/jquery.actual

【讨论】:

感谢您的链接!它基本上将元素附加到页面,获取大小,然后将其删除 - 我也不完全确定它是否甚至适用于未隐藏的元素(即 - 根本不在页面的 DOM 树中)。幸运的是,这个库对于我完全不相关的项目会派上用场。谢谢!【参考方案4】:

既然您要求相关答案...

如果您的 DOM 元素不根据其内容动态调整大小,我建议采用以下工作流程。

虽然它可以让你的 DOM 结构有点臃肿,但使用“容器”对象通常会更好。这些对象将没有边框、填充或边距以保持其 DOM 尺寸可预测。然后你可以强制孩子在它的容器范围内紧贴地展开。

您的容器对象将用于查询特定的尺寸和位置,因为没有额外的属性来解释尺寸失真。

http://jsfiddle.net/aywS6/

CSS

#container 
    position: absolute;
    margin:0px;
    padding:0px;
    width: 300px;
    height: 100px;
    background: #aaa;
    border: 0 none;


#subject 
    position: absolute;
    top: 0px;
    bottom: 0px;
    left: 0px;
    right: 0px;
    padding: 10px 20px;
    background: #aaf;

HTML

<div id="container">
    <div id="subject">
        contents...
    </div>
</div>

【讨论】:

【参考方案5】:

这里的问题是在页面上添加这个元素之前你无法获取元素大小。

在您的方法“constructElement”中,您将元素添加到根元素,但只是在加载时添加到页面。 您的属性“rootElement”没有设置大小属性。

如果您想操作这些属性,请重新考虑对象/方法的结构。

看看我的update of your fiddle

$(function ()

    var ex = new exampleElement("Hello", "Here's text");
    ex.constructElement();
    document.body.appendChild(ex.rootElement);

    $("<span/>")
        .text(" => Width is " + $("div").width() + ", and Height is " + $("div").height())
        .appendTo($("div p"));
);

【讨论】:

【参考方案6】:

不知道这是否有帮助,但既然您要求任何答案 :) 这就是我所拥有的。

我有一个类似的问题,我希望有一个很好的 jQuery 效果来根据它的内部内容将 HTML 容器的大小调整为“自动”高度和宽度。这个想法仍然类似于一些人已经在这里提到的:你在“屏幕外”的某个地方有一个容器,你把你的东西放在那里,测量尺寸并删除它。所以这是我找到的函数here

jQuery.fn.animateAuto = function(prop, speed, callback) 
    var elem, height, width;
    return this.each(function(i, el) 
        el = jQuery(el), elem = el.clone().css(
            "height" : "auto",
            "width" : "auto"
        ).appendTo("body");
        height = elem.css("height"), width = elem.css("width"), elem.remove();

        if (prop === "height")
            el.animate(
                "height" : height
            , speed, callback);
        else if (prop === "width")
            el.animate(
                "width" : width
            , speed, callback);
        else if (prop === "both")
            el.animate(
                "width" : width,
                "height" : height
            , speed, callback);
    );
;

这段代码比你需要的多一点,但你可能感兴趣的部分是:

elem = el.clone().css(
                "height" : "auto",
                "width" : "auto"
).appendTo("body");
height = elem.css("height")
width = elem.css("width")

希望对你有帮助

【讨论】:

【参考方案7】:

如果主要问题是在将其放置到屏幕上之前无法获得大小,我建议创建一个允许将 div 隐藏在页面背景下的类。这应该可以跨浏览器工作。

.exampleHidden
    position: absolute;
    z-index: -1;

如果您将页面的 z-index 的主背景设置为 0,然后将隐藏类附加到您的新元素,它会将其置于背景之下。假设它不大于​​您的背景,现在应该将其隐藏,以便您在通过删除 .exampleHidden 类将其实际带回前台之前测量和设置高度和宽度。

【讨论】:

以上是关于获取组件的实际宽度和高度的主要内容,如果未能解决你的问题,请参考以下文章

获取图片实际渲染的宽度高度与图片原始的宽度和高度

获取磁盘中图像的实际高度和宽度文件大小[重复]

JavaScript获取html元素的实际宽度和高度

php yii - 如何从 yii 图像组件获取图像宽度和高度属性 [关闭]

react-native 获取组件的宽度和高度

jquery outerHeight方法 outerWidth方法 获取元素实际宽度高度