我看到了一些奇怪的行为,只要用手指伸出手指时,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);
});
}
https://jsfiddle.net/rx1qj9l5/5/
它发出了AJAX请求(向不存在的URL)并收回错误。AJAX请求在scroll
事件上发出。当您使用鼠标滚动时,平均时间为80-200毫秒。这似乎非常稳定。
这是麻烦到达的地方。当用手指滚动时,ajax请求可能会陷入"下载内容" >直到手指从滚动中释放出来!
>使用以下步骤,我能够始终繁殖:
- 在启用触摸的设备(在Google Chrome中) 在上面的页面上重新加载
- 打开Chrome Dev工具,转到网络选项卡
- 降落可滚动的Div(不要放手!)握住手指时上下滚动。
- 这将发射3个Ajax呼叫,其中三分之一将无限地悬挂在您释放手指之前。这只有在Google Chrome内的手指滚动时才会发生。
- 您释放手指的瞬间,您会看到Ajax经历了。
我想不出任何原因的原因。有人遇到过吗?这个问题会有任何解决方法吗?
P.S。 - 我尝试在Vanilla JavaScript中进行相同的绑定。没有差异。:/
好吧,我找到了这种行为的原因。由于默认情况下的JavaScript是单线程,在滚动启用触摸设备时,该线程被暂停。(我觉得要禁用"事件的性质"应该解决该问题,但是该线程仍在仍然停了下来) 但是,有一个解决方案。如果您产生HTML5 Webworker,则 JavaScript不再是单个螺纹。Webworker获得了自己的线程,并且滚动不再引起任何问题。 在网络工作者中使用Ajax进行一些我自己的测试,Ajax始终如一地更快,并且可以完美地滚动。这是我见过的最顺畅的无限卷轴! 通过网络工作者的Ajax似乎在周围工作得更好(可能是因为它具有自己的专用线程吗?)但是,对于移动铬滚动,并在滚动后继续保持1-2秒的暂停。使用Web Worker允许Ajax在滚动甚至停止之前完成,因此它无限期地给出了无缝滚动的幻想。 这是我(依赖的免费)使用Web Worker调用AJAX函数的解决方案: Webworker具有我从jQuery源代码派生的代码,以允许在AJAX调用中使用JSON对象。我将源代码删除至最低限度。缩小之前的全尺寸网络工程师仅约80行。 一旦到位,您就可以这样做一个Ajax请求: 随意使用并适应此代码。主页的JS中的功能:
// wajax (webworker ajax)
function wajax(obj)
{
var sendObj = {};
sendObj.url = obj.url;
sendObj.data = obj.data;
sendObj.csrf = {{csrfToken here}}; //make sure to fill this out if you're using csrf tokens
if (typeof(Worker) !== "undefined") {
if (typeof(ajaxWorker) === "undefined") {
ajaxWorker = new Worker("path/to/webworker.js");
}
ajaxWorker.postMessage(JSON.stringify(sendObj));
ajaxWorker.onmessage = function(event) {
obj.success(event.data);
};
}
else {
// This means webworkers aren't available. Here, just do a regular ajax call....
}
}
WebWorker(与页面的JavaScript与单独的脚本/线程):
/* 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));
};
wajax({
url : "/some/url",
data : {value_a:"somestuff",value_b:2},
success: function(response) {
// do something with the response
}
});
我想不出这种行为的任何原因。
最明显的是减少用户滚动时运行的JavaScript量,以使滚动更加顺畅。当JavaScript运行时,它会阻止UI更新,这可能会导致一些Janky滚动(尤其是如果有一些重型DOM操纵)。
有人遇到过吗?
我发现有人提交的铬问题听起来像同一问题:settimeout和xhr请求在用户滚动时被忽略。看起来由于存在不确定性而关闭。
这个问题会有任何解决方法吗?
可能不是。
当用户在页面底部时,您需要触发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。很多时候,当您遇到卷轴jank时,罪魁祸首是一个触摸事件的听众。Chrome 51的新手被动活动听众是新兴的Web标准,可为滚动性能提供主要的潜在增强性,尤其是在移动设备上。
https://developers.google.com/web/updates/2016/06/passive-event-listeners
行为取决于您的计算机。看到您频繁地呼叫时,您正在耗尽系统的资源。对于使用低端设备的移动用户来说,这尤其令人讨厌。
在我的笔记本电脑上,每次滚动时,Ajax请求都会发射。但是,我确实很快就遭受了以下违规行为:
[违规]执行JavaScript时强制回转
这是由于您如此频繁地修改DOM的事实。请参阅强制布局/反流和布局thrashing 。
上的这篇文章您想做的是限制您要为每次滚动处理的代码。您可以通过 debouncing 进行此操作(又称函数执行的速率)。
您可以将具有此内置的Lodash之类的库导入,但是如果这是您唯一需要的地方,那可能会过高。幸运的是,编写我们自己的(基本)debounce
功能相对无痛:
function debounce(func, timeToWait) {
var timeout;
return function() {
clearTimeout(timeout);
timeout = setTimeout(func, timeToWait);
};
}
在问题的上下文中,您可以使用debounce
来限制infiniteDictionaryScrollAjax
的执行率:
var debouncedAjax = debounce(infiniteDictionaryScrollAjax, 500); // debounce here
$("div").on('scroll', debouncedAjax); // use debounced
function infiniteDictionaryScrollAjax() {
$("div").off("scroll");
$.ajax({
type: "POST",
url: "/someURL",
data: {
data: "data"
}
})
.done(function(response) {
$("div").append("<br>appendedData");
$("div").on('scroll', debouncedAjax); // use debounced
})
.fail(function() {
$("div").append("<br>appendedError");
$("div").on('scroll', debouncedAjax); // use debounced
});
}
function debounce(func, timeToWait) {
var timeout;
return function() {
clearTimeout(timeout);
timeout = setTimeout(func, timeToWait);
};
}
在此处查看工作JSFIDDLE。请注意,AJAX调用仅在用户暂停滚动后仅运行500毫秒(对于移动设备来说是完全合理的,因为用户将短暂暂停以在滚动时查看内容)。