JavaScript - 点击事件时加载/忙指示器或透明的页面覆盖层

9
我有一个客户端的JavaScript函数,它会在按钮点击时触发(基本上是一个计算器!)。有时候,由于页面上数据过多,JavaScript计算器函数需要太长时间来执行,这使得页面对用户不活跃。我打算在整个页面上显示一个透明的div,可能带有繁忙指示器(在中心),直到计算器函数结束,以便用户等待进程结束。
function CalculateAmountOnClick() {
  // 显示透明的div
// 我耗时的循环! {
}
// 移除透明的div
}
你有什么好的建议吗?当我的计算器函数开始运行时,我应该使用JavaScript为包围整个页面内容的div分配一个CSS类吗?我尝试过,但没有得到期望的结果。在IE6中存在透明度问题。另外,我该如何在这样一个透明的div中显示加载信息和图片?
谢谢。
5个回答

11

用JavaScript展示窗帘:

function CalculateAmountOnClick () {
  var curtain = document.body.appendChild( document.createElement('div') );
  curtain.id = "curtain";
  curtain.onkeypress = curtain.onclick = function(){ return false; }
  try {
    // your operations
  }
  finally {
    curtain.parentNode.removeChild( curtain );
  }
}

你的CSS:

#curtain {
  position: fixed;
  _position: absolute;
  z-index: 99;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  _height: expression(document.body.offsetHeight + "px");
  background: url(curtain.png);
  _background: url(curtain.gif);
}

(将 MSIE 6 的下划线 hack 移到需要条件包含的文件中。)

您可以将其设置为幕布的添加/删除函数,或作为包装器:

function modalProcess( callback ) {
  var ret;
  var curtain = document.body.appendChild( document.createElement('div') );
  curtain.id = "curtain";
  curtain.onkeypress = curtain.onclick = function(){ return false; }
  try {
    ret = callback();
  }
  finally {
    curtain.parentNode.removeChild( curtain );
  }
  return ret;
}

然后您可以像这样调用它:

var result = modalProcess(function(){
  // your operations here
});

5
我要做一些大胆的假设,但是我觉得发生的情况是,因为您在设置幕布元素后立即使用强烈的处理锁定了浏览器,所以浏览器从未有机会绘制幕布。
浏览器不会在每次更新DOM时重新绘制。它可能会等待看看您是否正在执行更多操作,然后绘制所需的内容(各种浏览器对此的方法有所不同)。因此,在这种情况下,它可能只在删除幕布之后刷新显示,或者您通过滚动强制重新绘制。
公平警告:这种强烈的处理方式不太好,因为它不仅锁定了您的页面。由于浏览器通常为所有选项卡实现单个Javascript线程,因此您的处理将锁定所有打开的选项卡(=浏览器)。此外,您还面临执行超时和浏览器简单停止脚本的风险(这可能低至5秒)。
以下是解决方法。
如果您可以将处理过程分成较小的块,则可以使用超时运行它(以允许浏览器有呼吸空间)。像这样的东西应该有效:
function processLoop( actionFunc, numTimes, doneFunc ) {
  var i = 0;
  var f = function () {
    if (i < numTimes) {
      actionFunc( i++ );  // closure on i
      setTimeout( f, 10 )
    } 
    else if (doneFunc) { 
      doneFunc();
    }
  };
  f();
}

// add a curtain here
processLoop(function (i){
  // loop code goes in here
  console.log('number: ', i);
}, 
10,  // how many times to run loop
function (){
  // things that happen after the processing is done go here
  console.log('done!');
  // remove curtain here
});

这实质上是一个 while 循环,但每次迭代都在定时间隔内完成,这样浏览器就有一点时间在其中呼吸。这会减慢处理速度,后续的任何工作都需要进入回调函数,因为循环独立于可能跟随 processLoop 调用的内容。
另一种变化是设置帷幕,使用 setTimeout 调用处理函数,以便让浏览器有时间绘制帷幕,然后在完成后删除它。
// add a curtain
var curtain = document.body.appendChild( document.createElement('div') );
curtain.id = "curtain";
curtain.onkeypress = curtain.onclick = function(){ return false; }
// delay running processing
setTimeout(function(){
  try {
    // here we go...
    myHeavyProcessingFunction();
  }
  finally {
    // remove the curtain
    curtain.parentNode.removeChild( curtain );
  }
}, 40);

如果您正在使用JavaScript库,您可能希望考虑使用现成的解决方案来创建窗帘。大多数库都应该有这样的解决方案,这里是一个针对jQuery的解决方案,它们可以帮助处理CSS。

2
我会这样做:
  1. 取消隐藏div元素(使用"display:inline")
  2. 将其定位为绝对定位("position:absolute")
  3. 设置z-index值为99("z-index:99")
  4. 将高度和宽度设置为100%("height:100%; width:100%")
  5. 处理完成后,将display属性设置为none("display:none")
要使其透明,您需要设置不同浏览器中的不同透明度,如Firefox、IE等。
如果要显示加载图标,您可以创建第二个div,并在页面上放置它。当加载完成后,删除它以及透明的div元素。

在我的情况下,第四点没有起作用。通过 jQuery,我使 div 的宽度和高度与父元素相同,所有的都运行良好!其他点完全没问题! - Neonamu

0
除了以上所有内容,不要忘记在 shim 后面放置一个不可见的 iframe,这样它就会显示在 IE 的选择框上方。
编辑: 虽然这个网站提供了解决更复杂问题的解决方案,但也涵盖了创建模态背景。 http://www.codeproject.com/KB/aspnet/ModalDialogV2.aspx

0
对于加载消息,我会使用一个带有position:absolute<div>,使用lefttop进行定位,并将显示设置为none
当您想要显示加载指示器时,必须使用超时,否则div将不会显示,直到处理完成。因此,您应该将代码修改为以下内容:
function showLoadingIndicator()
{
    // Display div by setting display to 'inline'
   setTimeout(CalculateAmountOnClick,0);
}

function CalculateAmountOnClick()
{
  // MY time consuming loop!
    {
    }
  // Remove transparent div 
}

因为您设置了超时时间,所以在耗时循环发生之前,页面将重新绘制。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接