scrollHeight的替代DOM属性

时间:2015-10-07 06:31:14

标签: javascript html knockout.js autoresize

有没有其他方法可以在不使用scrollHeight的情况下获取某个HTML元素的内部高度?

事情是我有一个包含100条记录的表,并且在每条记录中我使用height = 0然后height = scrollHeight调整列中某个textarea的大小。

问题是IE执行scrollHeight至少20~50ms取决于大小而我有大约100条记录。这意味着,仅仅为IE渲染所有记录,只需加载100条记录就需要6秒以上,如果我需要加载100条记录呢?这会破坏我的网站。

所以我问是否有任何替代scrollHeight或者你可以提供任何替代代码来调整textarea的大小。

谢谢提前,

更新

这是我从IE UI响应中得到的 enter image description here

更新2

第二张图片:正如你在下面的图片中看到的那样,为什么IE在获取某个元素(textarea)的scrollHeight方面很慢的问题是因为它计算整个布局(html正文)而不是仅仅获取scrollHeight该特定元素(textarea元素)

enter image description here

1 个答案:

答案 0 :(得分:5)

听起来你要一行一行,阅读scrollHeight然后设置height。这是非常昂贵的,因为写入后的每次读取都会触发回流以便给出准确的答案(因为写入可能会改变页面上会影响正在读取的值的内容)。花费时间不是height=scrollHeight;它是必要的回流,以便它做到这一点。

layout (write height of all: 0)

reflow
read scrollHeight
write height

reflow (!)
read scrollHeight
write height

reflow (!)
read scrollHeight
write height
...

reflow (!)
paint

(或者你可能在每次迭代中将height设置为0;它并不重要,因为连续写入不会触发重排。)

相反,请尝试批量执行此操作。将所有scrollHeight值读入数组;然后,当你拥有它们时,将它们全部设置好。通过这种方式,您只能获得两次回流:一次开始阅读,一次完成代码并且浏览器需要显示页面,因为只要您只是阅读或只是写作,就不会有回流需要 - 只有当你从写作到阅读价值时才会发生。

layout (write height of all: 0)

reflow
read scrollHeight
read scrollHeight
...
write height
write height

reflow
paint

编辑:这是一个黑客;我还没有在Knockout那么好。但作为可能的最小例子,如果你不能以任何其他方式做到这一点,它可能会做。您可能希望运行它并延伸到"整页",或者您无法看到任何内容:)



var vm = {
  list: ko.observableArray(["foo", "bar\nbaz", "1\n2\n3\n4"])
};

var updatedHeights;

ko.bindingHandlers.autoresizeStart = {
  update: function(elem, valueAccessor) {
    var data = ko.utils.unwrapObservable(valueAccessor());
    console.log("start update; setting updateHeights to []");
    updatedHeights = [];
  }
};
ko.bindingHandlers.autoresize = {
  update: function(elem, valueAccessor) {
    console.log("mid update;", elem, "is updated, so let's remember its scrollHeight");
    updatedHeights.push([elem, elem.scrollHeight]);
  }
};
ko.bindingHandlers.autoresizeEnd = {
  update: function(elem, valueAccessor) {
    var data = ko.utils.unwrapObservable(valueAccessor());
    console.log("end update; lets set heights for all updated elements:");
    updatedHeights.forEach(function(elemWithHeight) {
      console.log("  ", elemWithHeight[1], elemWithHeight[0]);
      elemWithHeight[0].style.height = elemWithHeight[1] + "px";
    });
    console.log("all done :)");
  }
};

ko.virtualElements.allowedBindings.autoresizeStart = true;
ko.virtualElements.allowedBindings.autoresizeEnd = true;


ko.applyBindings(vm);

setTimeout(function() {
  vm.list.push("a\nb\nc\nd\ne\nf\ng");
}, 1000);

<!-- results pane console output; see http://meta.stackexchange.com/a/242491 -->
<script src="http://gh-canon.github.io/stack-snippet-console/console.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>

<!-- ko autoresizeStart: list --><!-- /ko -->
<ul data-bind="foreach: list">
  <li >
    <textarea data-bind="text: $data, autoresize: $data"></textarea>
  </li>
</ul>
<!-- ko autoresizeEnd: list --><!-- /ko -->
&#13;
&#13;
&#13;