Javascript 触摸屏上的滚动大大降低了在Chrome上下载AJAX的速度

Javascript 触摸屏上的滚动大大降低了在Chrome上下载AJAX的速度,javascript,jquery,ajax,google-chrome,Javascript,Jquery,Ajax,Google Chrome,我看到一些奇怪的行为,当用手指滚动时,只要按住手指,AJAX请求就会偶尔挂起。很难用语言来描述这个问题,所以看看这把小提琴: $("div").on('scroll', infiniteDictionaryScrollAjax); function infiniteDictionaryScrollAjax(){ $("div").off("scroll"); $.ajax({ type: "POST", url : "/someURL", data:

我看到一些奇怪的行为,当用手指滚动时,只要按住手指,AJAX请求就会偶尔挂起。很难用语言来描述这个问题,所以看看这把小提琴:

$("div").on('scroll', infiniteDictionaryScrollAjax);

function infiniteDictionaryScrollAjax(){
$("div").off("scroll");
$.ajax({
       type: "POST",
       url : "/someURL",
       data: {data: "data"}

     })
     .done(function(response) {
       $("div").append("<br>appendedData");
       $("div").on('scroll', infiniteDictionaryScrollAjax);
     })
     .fail(function() {
       $("div").append("<br>appendedError");
       $("div").on('scroll', infiniteDictionaryScrollAjax);
     });
  }
它向不存在的URL发出AJAX请求并返回错误。AJAX请求在scroll事件中触发。使用鼠标滚动时,平均时间在80-200毫秒之间。这似乎非常一致

麻烦就在这里。当用手指滚动时,ajax请求可能会陷入无法确定的下载内容中,直到手指从滚动中释放出来

通过以下步骤,我能够始终如一地复制:

在谷歌Chrome的触控设备上重新加载上面的页面 打开chrome开发工具,转到网络选项卡 触摸可滚动的div,不要放手!按住手指上下滚动。 这将触发3个ajax调用,第三个调用将无限挂起,直到您松开手指为止。只有在谷歌浏览器中用手指滚动时才会发生这种情况。 在您松开手指的那一刻,您将看到Ajax已经完成。 我想不出这种行为的任何原因。有人遇到过这种情况吗?这个问题有什么解决办法吗

p、 我试过用香草Javascript做同样的绑定。没有区别/

我想不出这种行为的任何原因

这很可能是为了减少用户滚动时运行的JavaScript量,从而使滚动更加平滑。当JavaScript运行时,它会阻止UI更新,这可能会导致一些不方便的滚动,特别是在有一些严重的DOM操作的情况下

有人遇到过这种情况吗

我发现有人提交了一个铬问题,听起来像是同一个问题:。看起来它关闭了,因为不确定是否存在

这个问题有什么解决办法吗

可能不会

我想不出这种行为的任何原因

这很可能是为了减少用户滚动时运行的JavaScript量,从而使滚动更加平滑。当JavaScript运行时,它会阻止UI更新,这可能会导致一些不方便的滚动,特别是在有一些严重的DOM操作的情况下

有人遇到过这种情况吗

我发现有人提交了一个铬问题,听起来像是同一个问题:。看起来它关闭了,因为不确定是否存在

这个问题有什么解决办法吗


可能不会。当用户位于页面底部时,您需要触发ajax

$(window).scroll(function() {
   if($(window).scrollTop() + $(window).height() == $(document).height()) {
       $.ajax({
       type: "POST",
       url : "/someURL",
       data: {data: "data"}

     })
     .done(function(response) {
       $("div").append("<br>appendedData");
     })
     .fail(function() {
       $("div").append("<br>appendedError");
     });
   }
});

当用户位于页面底部时,需要触发ajax

$(window).scroll(function() {
   if($(window).scrollTop() + $(window).height() == $(document).height()) {
       $.ajax({
       type: "POST",
       url : "/someURL",
       data: {data: "data"}

     })
     .done(function(response) {
       $("div").append("<br>appendedData");
     })
     .fail(function() {
       $("div").append("<br>appendedError");
     });
   }
});

这可能值得研究:

使用被动事件侦听器提高滚动性能

当你滚动一个页面时,会出现这样的延迟,以至于页面感觉不到固定在你的手指上,这就是所谓的滚动jank。很多时候,当你遇到scroll jank时,罪魁祸首是一个触摸事件监听器。对于Chrome51来说,被动事件监听器是一种新兴的网络标准,它可以极大地提高滚动性能,尤其是在移动设备上


这可能值得研究:

使用被动事件侦听器提高滚动性能

当你滚动一个页面时,会出现这样的延迟,以至于页面感觉不到固定在你的手指上,这就是所谓的滚动jank。很多时候,当你遇到scroll jank时,罪魁祸首是一个触摸事件监听器。对于Chrome51来说,被动事件监听器是一种新兴的网络标准,它可以极大地提高滚动性能,尤其是在移动设备上


该行为取决于您的机器。由于您频繁地进行AJAX调用,您正在耗尽系统资源。这对于使用低端设备的移动用户来说尤其令人讨厌

在我的笔记本电脑上,每次我滚动时都会触发AJAX请求。但是,我很快就收到了以下违规行为:

[冲突]执行JavaScript时强制回流

这是因为您经常修改DOM。请参阅关于强制布局/回流和布局抖动的说明

相反,您要做的是限制您正在为每个滚动处理的代码。您可以通过取消函数的抖动(也称为限制函数的执行速率)来实现这一点

您可以导入一个库,比如,它有这样的功能,但是如果这是您唯一需要它的地方,那么这可能太过分了。幸运的是,编写我们自己的基本去盎司函数相对来说比较轻松:

function debounce(func, timeToWait) {

    var timeout;

    return function() {

        clearTimeout(timeout);
        timeout = setTimeout(func, timeToWait);
    };
}
针对您的问题,您可以使用debounce来限制infiniteDictionaryScrollAjax的执行率:

看到一个正在工作的人。请注意,AJAX调用在用户暂停滚动后仅运行500毫秒,这对于移动设备来说是完全合理的,因为用户将短暂暂停以查看cont
当他们滚动的时候,他们会发出声音

行为取决于您的机器。由于您频繁地进行AJAX调用,您正在耗尽系统资源。这对于使用低端设备的移动用户来说尤其令人讨厌

在我的笔记本电脑上,每次我滚动时都会触发AJAX请求。但是,我很快就收到了以下违规行为:

[冲突]执行JavaScript时强制回流

这是因为您经常修改DOM。请参阅关于强制布局/回流和布局抖动的说明

相反,您要做的是限制您正在为每个滚动处理的代码。您可以通过取消函数的抖动(也称为限制函数的执行速率)来实现这一点

您可以导入一个库,比如,它有这样的功能,但是如果这是您唯一需要它的地方,那么这可能太过分了。幸运的是,编写我们自己的基本去盎司函数相对来说比较轻松:

function debounce(func, timeToWait) {

    var timeout;

    return function() {

        clearTimeout(timeout);
        timeout = setTimeout(func, timeToWait);
    };
}
针对您的问题,您可以使用debounce来限制infiniteDictionaryScrollAjax的执行率:


看到一个正在工作的人。请注意,AJAX调用仅在用户暂停滚动500毫秒后运行,这对于移动设备来说是完全合理的,因为用户在滚动时会短暂暂停以查看内容

好的,我找到了这种行为的原因。由于默认情况下JavaScript是单线程的,当在支持触摸的设备上滚动时,线程会暂停。我觉得禁用事件的被动性质应该可以解决问题,但线程仍然暂停

然而,有一个解决办法。如果你产生了一个HTML5WebWorker,JavaScript就不再是单线程的。WebWorker拥有自己的线程,滚动不再引起任何问题

在webworker中使用ajax进行一些我自己的测试时,ajax始终更快,并且可以完美地使用滚动。这是我见过的最平滑的无限卷轴

通过webworker的ajax似乎在各个方面都运行得更好,可能是因为它有自己的专用线程?但特别是对于移动chrome,ajax在滚动期间暂停,并在滚动后继续保持1-2秒的暂停。使用web worker允许ajax在滚动停止之前完成,因此它产生了无限期无缝滚动的错觉

以下是我的无依赖解决方案,用于使用web worker调用ajax函数:

主页js中的函数: Webworker将脚本/线程与页面的javascript分开: webworker有我从jQuery源代码派生的代码,允许在AJAX调用中使用JSON对象。我把源代码精简到最低限度。在缩小之前,webworker的完整尺寸仅为80行左右

/* ADAPTED FROM JQUERY SOURCE */
function param(a)
{
  var prefix,
      params = [],
      add    = function(key, value) {
        params[params.length] = encodeURIComponent(key) + "=" + encodeURIComponent(value == null ? "" : value);
      };

  for (prefix in a) {
    buildParams(prefix, a[prefix], add);
  }

  // Return the resulting serialization
  return params.join("&");
}

function buildParams(mainKey, mainValue, add)
{
  var name;
  var length = mainValue.length;
  if (Array.isArray(mainValue)) {
    for (var index = 0; index < length; index++) {
      var value = mainKey[index];
      if (/\[]$/.test(mainKey)) {
        add(mainKey, value);
      }
      else {
        buildParams(mainKey + "[" + (typeof value === "object" && value != null ? index : "") + "]", value, add);
      }
    }
  }
  else if (typeof mainValue === "object") {
    for (name in mainValue) {
      buildParams(mainKey + "[" + name + "]", mainValue[name], add);
    }
  }
  else {
    add(mainKey, mainValue);
  }
}

/* END CODE ADAPTED FROM JQUERY SOURCE */

/*
* Created by Skeets 2017-12-13
* */

onmessage = function(e) {

  var obj = JSON.parse(e.data);

  obj.data._token = obj.csrf;

  var request = new XMLHttpRequest();
  request.open('POST', obj.url, true);
  request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');

  request.onload = function() {
    if (request.status >= 200 && request.status < 400) {
      // Success
      var output;
      try {
        output = JSON.parse(request.responseText);
      }
      catch (e) {
        output = request.responseText;
      }
      postMessage(output);
    }
    else {
      // error
      console.log(request.responseText);
    }
  };

  request.onerror = function() {
    // connection error
  };

  request.send(param(obj.data));

};

请随意使用和修改此代码。

好的,我找到了这种行为的原因。由于默认情况下JavaScript是单线程的,当在支持触摸的设备上滚动时,线程会暂停。我觉得禁用事件的被动性质应该可以解决问题,但线程仍然暂停

然而,有一个解决办法。如果你产生了一个HTML5WebWorker,JavaScript就不再是单线程的。WebWorker拥有自己的线程,滚动不再引起任何问题

在webworker中使用ajax进行一些我自己的测试时,ajax始终更快,并且可以完美地使用滚动。这是我见过的最平滑的无限卷轴

通过webworker的ajax似乎在各个方面都运行得更好,可能是因为它有自己的专用线程?但特别是对于移动chrome,ajax在滚动期间暂停,并在滚动后继续保持1-2秒的暂停。使用web worker允许ajax在滚动停止之前完成,因此它产生了无限期无缝滚动的错觉

以下是我的无依赖解决方案,用于使用web worker调用ajax函数:

主页js中的函数: Webworker将脚本/线程与页面的javascript分开: webworker有我从jQuery源代码派生的代码,允许在AJAX调用中使用JSON对象。我把源代码精简到最低限度。在缩小之前,webworker的完整尺寸仅为80行左右

/* ADAPTED FROM JQUERY SOURCE */
function param(a)
{
  var prefix,
      params = [],
      add    = function(key, value) {
        params[params.length] = encodeURIComponent(key) + "=" + encodeURIComponent(value == null ? "" : value);
      };

  for (prefix in a) {
    buildParams(prefix, a[prefix], add);
  }

  // Return the resulting serialization
  return params.join("&");
}

function buildParams(mainKey, mainValue, add)
{
  var name;
  var length = mainValue.length;
  if (Array.isArray(mainValue)) {
    for (var index = 0; index < length; index++) {
      var value = mainKey[index];
      if (/\[]$/.test(mainKey)) {
        add(mainKey, value);
      }
      else {
        buildParams(mainKey + "[" + (typeof value === "object" && value != null ? index : "") + "]", value, add);
      }
    }
  }
  else if (typeof mainValue === "object") {
    for (name in mainValue) {
      buildParams(mainKey + "[" + name + "]", mainValue[name], add);
    }
  }
  else {
    add(mainKey, mainValue);
  }
}

/* END CODE ADAPTED FROM JQUERY SOURCE */

/*
* Created by Skeets 2017-12-13
* */

onmessage = function(e) {

  var obj = JSON.parse(e.data);

  obj.data._token = obj.csrf;

  var request = new XMLHttpRequest();
  request.open('POST', obj.url, true);
  request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');

  request.onload = function() {
    if (request.status >= 200 && request.status < 400) {
      // Success
      var output;
      try {
        output = JSON.parse(request.responseText);
      }
      catch (e) {
        output = request.responseText;
      }
      postMessage(output);
    }
    else {
      // error
      console.log(request.responseText);
    }
  };

  request.onerror = function() {
    // connection error
  };

  request.send(param(obj.data));

};

请随意使用和调整此代码。

在滚动事件上使用ajaxing是一个糟糕的选择idea@madalinivascu好吧,那么facebook是如何做到的呢?还是谷歌+?或任何其他具有无限滚动的页面?它检查用户是否已到达页面底部,然后触发ajax@madalinivascu是的,就是这个主意。这里的代码被缩减到最低限度,以表达问题的性质。另外,如果语句不能解决问题,我希望它能解决,它只会使错误更难再现五分之一,而不是每次对滚动事件进行调整都是一个糟糕的错误idea@madalinivascu好吧,那么facebook是如何做到的呢?还是谷歌+?或者任何其他具有无限滚动的页面?它检查用户是否已到达t的底部

然后触发ajax@madalinivascu是的,就是这个主意。这里的代码被缩减到最低限度,以表达问题的性质。补充说,如果语句不能解决问题,我希望它能解决,它只会使错误更难再现五分之一,而不是每次我都可以使用以下确切的代码再现错误:当ajax请求无法无限期地下载内容时,您能提供xhr请求的屏幕截图吗?触摸事件不应该与ajax请求,从屏幕截图中我可以看到你的请求转到404,你能公布你请求的时间吗?通常,在触发ajax后,您会移除手指。您的实现应该执行类似于在移动设备中刷新页面的操作,您可以向下滑动,然后释放页面进行刷新。在您的情况下,可以从ajax中附加html,如果您不释放点击,页面不会刷新。我可以使用以下代码重现错误:当ajax请求无法无限期下载内容时,您能否提供xhr请求的屏幕截图?触摸事件不应与ajax请求交互,从屏幕截图中我可以看到您的请求转到404,你能公布你请求的时间吗?通常,在触发ajax后,您会移除手指。您的实现应该执行类似于您在移动设备中刷新页面的操作,您可以向下滑动,然后释放页面以刷新页面。在您的情况下,从ajax中附加html,如果您不释放点击,则页面不会刷新这听起来很有希望,但不幸的是,将事件设置为非被动事件默认设置为被动事件并没有解决问题:/这听起来很有希望,但不幸的是,将事件设置为非被动事件默认设置为被动事件并没有解决问题:/我投赞成票,因为通过阅读,这可能是答案。不幸的是,问题似乎出在其他地方。这个请求是一个ajax请求,它不会以任何方式压倒系统,它在下载内容阶段不确定地挂起,要下载的内容只有10KB左右,我一把手指从屏幕上拿开,它就通过了。上面的代码是在我的机器上重现问题所需的最低限度。我投票赞成,因为通过阅读,它可能是答案。不幸的是,问题似乎出在其他地方。这个请求是一个ajax请求,它不会以任何方式压倒系统,它在下载内容阶段不确定地挂起,要下载的内容只有10KB左右,我一把手指从屏幕上拿开,它就通过了。上面的代码是在我的机器上重现问题所需的最低限度。您报告的这个问题也出现在桌面上,如果您向下滚动并保持鼠标滚动按钮上下移动,ajax就会被卡住,只要您继续这样做,另外,如果您只是非常快速地向下滚动,那么每3-4次ajax调用就会被卡住2-5秒,并中断平滑滚动。在第一次实现时,您是否注意到桌面上出现了这种情况?我无法在我的系统上复制它,但如果Mac桌面上出现类似问题,如果浏览器中出现平滑滚动,我也不会感到惊讶,因为我对Mac桌面不太熟悉。然而,在这里使用webworker绝对是正确的答案。无论是台式机、移动设备还是其他设备,它的运行都会更加顺畅。你的无限滚动得到它自己的不间断线程。我测试过苹果、安卓、windows、低端设备和所有主要浏览器。每次运行都更平稳。另外,其他答案被否决的唯一原因是因为我否决了它们P@nikocraft好的,我已经编辑了我的答案,包括完整的代码示例。没有库依赖性,所以它应该适合您。只是为了确认我让web worker使用vuejs,现在我获得了平滑的几乎即时无限滚动,太棒了!感谢Skeets发表这篇文章,我甚至没有意识到这件事会成为一个问题,为此我挠头了好几天。不,你不是疯了,我想我和我的编码合作伙伴疯了,我想知道世界上怎么没有人碰到这个问题,我很高兴你7个月前就这样做并发表了相关文章!:您报告的这个问题也出现在桌面上,如果您向下滚动并保持鼠标滚动按钮上下移动,ajax就会被卡住,只要您一直这样做,如果您只是非常快速地向下滚动,每3-4次ajax调用就会被卡住2-5秒,并中断平滑滚动。在第一次实现时,您是否注意到桌面上出现了这种情况?我无法在我的系统上复制它,但如果Mac桌面上出现类似问题,如果浏览器中出现平滑滚动,我也不会感到惊讶,因为我对Mac桌面不太熟悉。然而,在这里使用webworker绝对是正确的答案。无论是台式机、移动设备还是其他设备,它的运行都会更加顺畅。你的无限滚动得到它自己的不间断线程。我测试过苹果、安卓、windows、低端设备和所有主要浏览器。跑步
每一次都是驼背。另外,其他答案被否决的唯一原因是因为我否决了它们P@nikocraft好的,我已经编辑了我的答案,包括完整的代码示例。没有库依赖性,所以它应该适合您。只是为了确认我让web worker使用vuejs,现在我获得了平滑的几乎即时无限滚动,太棒了!感谢Skeets发表这篇文章,我甚至没有意识到这件事会成为一个问题,为此我挠头了好几天。不,你不是疯了,我想我和我的编码合作伙伴疯了,我想知道世界上怎么没有人碰到这个问题,我很高兴你7个月前就这样做并发表了相关文章!: