当列表包含太多dom元素时,页面会冻结标签

时间:2017-01-27 15:33:40

标签: html css dom optimization responsive-design

我有一个包含两列的网页,一个带导航栏和页脚的标题。

左列用于列出三个不同标签中的项目。每个选项卡都包含其项目的类型。

右侧包含一个或多个地图以显示项目。

选择包含太多元素的选项卡时会出现问题。

它冻结了交互(突出显示,添加/删除DOM和动画),打破了它的响应能力。

即使它不是与所选标签的交互(即鼠标悬停在导航栏链接上)。

但是当所选标签的项目较少时,页面的响应能力很好。

我创建了一个spike solution来向您展示我在说什么。

请你记住,这是我问题的一个简单版本。这只是一个虚拟的例子来陈述我的案例。

$('#nav-tabs a').click(function (e) {
  e.preventDefault()
  $(this).tab('show')
});

$('#addBox').on('click', function () {
  $("#content").append("<div class='box pull-left'></div>");
});

$('#newPapper').on('click', function () {
  $("#content").empty();
});


$('#addOne').on('click', function () {
  $("#home div.panel-default").append(createContactDom());
});

$('#addThousand').on('click', function () {
  var dom = "";
  for(var i = 5000; i > 0; i--){
    dom+=createContactDom();
  }
  $("#home div.panel-default").append(dom);
});

$('#clean').on('click', function () {
  $("#home div.panel-default").empty();
});

function createContactDom(){

  var age = Math.round(Math.random()*100);
  var birthday = moment().subtract(age, 'years');
  var isFemale = Math.random() > 0.4;
  var nameIndex = Math.floor(Math.random() * names[isFemale ? "female" : "male"].length);
  var surnameIndex = Math.floor(Math.random() * surnames.length)
  var name = names[isFemale ? "female" : "male"][nameIndex] + " " + surnames[surnameIndex];
  
  var html = '<div id="p' + birthday.format("X") + '" class="panel-heading">';
  html += '<span class="fa-stack fa-lg custom-stack pull-left font-grey-gallery" >';
  html += '<i class="fa fa-square-o fa-stack-2x"></i>';
  html += '<i class="fa fa-user fa-stack-1x"></i>';
  html += '</span>';
  html += '<div class="pull-left">';
  html += '<div class="title">';
  html += '<span >' + name + '</span> ';
  html += '</div>';
  html += '<div class="sub-title">';
  html += '<span title="' + (isFemale ? 'She': 'He') + ' was born on a ' + birthday.format('dddd') + ' at ' + birthday.format('HH:MM a') + '" class="badge pull-left" >' + birthday.format("YYYY/MM/DD") +'</span>';
  html += '<span title="It is a ' + (isFemale ? 'female' : 'male') + '" class="badge pull-left ' + (isFemale ? 'female' : 'male') + '" >' + (isFemale ? 'Female' : 'Male') + '</span>';
  html += '<div class="clearfix"></div>';
  html += '</div>';
  html += '</div>';
  html += '<div class="pull-right actions">';
  html += '<a id="d' + birthday.format("X") + '" title="Delete contact" class="fa fa-times fa-times-close"  style="color: rgb(87, 142, 190);" onclick="deleteContact(this)"></a>';
  html += '</div>';
  html += '<div class="clearfix"></div>';
  html += '</div>';
  
	return html;                      
}


deleteContact = function(e){
  $("#" + e.id.replace("d", "p")).remove();
}

var names = {
  female: ["Maria","Leonor","Matilde","Beatriz","Carolina","Mariana","Ana","Inês","Margarida","Sofia"],
  male: ["João", "Martim", "Rodrigo", "Santiago", "Francisco", "Afonso", "Tomás", "Miguel", "Guilherme", "Gabriel"]
}
var surnames = ["Silva", "Santos", "Ferreira", "Pereira", "Oliveira", "Costa", "Rodrigues", "Martins", "Jesus", "Sousa", "Fernandes", "Gonçalves", "Gomes", "Lopes", "Marques", "Alves", "Almeida", "Ribeiro", "Pinto", "Carvalho"]
.row{
  height: 600px;
}

.col-sm-5,
.col-sm-7{
  height: inherit;
}

.tab-content{
  height: inherit;
  overflow-y: auto;  
}

button{
  margin-top: 5px;
}
span.badge{
  margin-right: 5px;
}

.badge{
   background-color: #999 !important;
}
.badge.female{
   background-color: pink !important;
}
.badge.male{
   background-color: #1c90f3 !important;
}

#content{
  height: inherit;
  overflow-y: auto;  
  padding-top: 5px;
}

#content .box{
  width: 100px;
  height: 100px;
  background-color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="row">
  <div class="col-sm-5">
    <ul class="nav nav-tabs" id="myTabs" role="tablist"> 
      <li role="presentation" class="active">
        <a href="#home" id="home-tab" role="tab" data-toggle="tab" aria-controls="home" aria-expanded="true">Contacts</a>
      </li> 
      <li role="presentation" class="">
        <a href="#profile" role="tab" id="profile-tab" data-toggle="tab" aria-controls="profile" aria-expanded="false">Problem?</a>
      </li> 
      <li>
        <button type="button" id="addOne" class="btn btn-sm">+</button>
        <button type="button" id="addThousand" class="btn btn-sm btn-primary">+5k</button>
        <button type="button" id="clean" class="btn btn-sm">clean</button>
      </li>
    </ul>

    <div class="tab-content" id="myTabContent"> 
      <div class="tab-pane fade active in" role="tabpanel" id="home" aria-labelledby="home-tab">
        <div class="panel panel-default" style="border-left: 3px solid rgb(87, 142, 190);" >
        </div> 
      </div> 
         
      <div class="tab-pane fade" role="tabpanel" id="profile" aria-labelledby="profile-tab"> 
        <h1>Problems to solve:</h1>
        <ul>
          <li>Adding 5k new contacts blocks the browser's page</li>
          <li>Switching between the two tabs blocks the browser's page</li>
          <li>Painting freezes when adding 5k new contacts</li>
        </ul> 
        <p>Note: The problems get worse as may contacts you add...</p>
      </div>
    </div>
  </div>

  <div class="col-sm-7">
    <button tyoe="button" id="addBox" class="btn btn-sm btn-primary">Paint</button>
    <button tyoe="button" id="newPapper" class="btn btn-sm">New papper</button>
    <div id="content"></div>
  </div>
  
</div>

我已经在使用Chrome开发者工具捕获时间轴的操作。到目前为止,我可以看到,问题是jquery的布局渲染中的一些回流。

以下是我的时间轴的截图: enter image description here

如何优化DOM操作以避免页面冻结?在我这样说之前,我可以告诉浏览器不要重绘左栏吗?

2 个答案:

答案 0 :(得分:5)

为什么不对这样的大表使用虚拟滚动无限滚动分页,而不是在启动?

有很多例子: https://www.sitepoint.com/jquery-infinite-scrolling-demos/

将5k个物体放在一行中太多了,imo。

在你的情况下,你需要在堆栈/事件循环中拆分/中断dom操作循环以用于其他东西。

$('#addThousand').on('click', function () {
  for(var i = 5000; i > 0; i--){
  setTimeout( function() {
    var dom=createContactDom();
    $("#home div.panel-default").append(dom);
    }, 0);
  }

});

或事件:

$('#addThousand').on('click', function () {
  for(var i = 5000; i > 0; i--){
  setTimeout( function() {
    var dom=createContactDom();
    $("#home div.panel-default").append(dom);
    }, i);
  }

});

答案 1 :(得分:3)

fast interacting with large number of elements inside a container (DOM, javascript)

是的,我确实做了一些测试和研究......这引导我做到这一点。

你需要使用类似下面的东西。问题是display none和block会根据您隐藏/显示的数据量对dom性能产生影响。

解决这个问题的方法就是将其移出视野。这样,如果内容存在或不存在,dom就不必运行计算。因为从技术上讲,它从未离开这样做。向下是你需要实现自己的标签..苏打水说能够覆盖默认行为。

div {
  position: relative;
  left: 0px;
}
div.hide {
  left: -4096px;
  height: 0px;
}