获取组件的实际宽度和高度
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 类将其实际带回前台之前测量和设置高度和宽度。
【讨论】:
以上是关于获取组件的实际宽度和高度的主要内容,如果未能解决你的问题,请参考以下文章