我正在构建一个 AJAX 实时搜索页面。到目前为止,一切都按预期工作,但我注意到我正在进行大量的 AJAX 调用。
我知道发生这种情况的地点和原因,但我找不到阻止这些 AJAX 调用发生的方法。
我会尽量给出一个快速的解释,然后粘贴下面的代码。
在页面的左侧,我有一堆过滤器(例如位置、半径、兴趣、参与度、日期范围等)。他们每个人都有一个事件监听器(例如更改)。当其中一个监听器被触发时,我更新查询参数并通过 AJAX 请求结果。
现在问题出现了,例如有人选择了 10 个兴趣(共有 36 个),然后他分享了 URL,它看起来像这样:
现在首先,我会说我正在为我的复选框使用 iCheck.js。这意味着检查“ifChecked”事件。 由于列表包含 10 个兴趣条目,“ifChecked”事件将被触发 10 次,从而导致 10 个 AJAX 请求。现在将其与 5 个 SDG、2 个参与、3 个位置……结合考虑,我最终得到大量 AJAX 请求。 同时删除我所有的兴趣(有一个“删除”链接)时,它会触发“ifUnchecked”事件 10 次,从而再次执行 10 个 AJAX 请求。
这是我的完整 JS 代码,我已经尝试使用 HTML 和所有内容创建一个 jsfiddle,但是该代码有点与 CodeIgniter 框架混合在一起,很难放在其中。但是 JS 代码足以让图片清晰可见。
//Set vars available to entire scope for filtering
var searchLocation = null;
var searchRadius = 0;
var searchMin = 0;
var searchMax = 1000;
var searchMinPrice = 0;
var searchMaxPrice = 50000;
var searchSelectedInterests = [];
var searchSelectedSdgs = [];
var searchStartDate = moment().format('YYYY-MM-DD');
var searchEndDate = moment().add(1, 'years').format('YYYY-MM-DD');
var searchSelectedEngagements = [];
var getUrl = window.location;
var baseUrl = getUrl .protocol + "//" + getUrl.host + "/" + getUrl.pathname.split('/')[1];
$(document).ready(function(){
'use strict';
// Square Checkbox & Radio
$('.skin-square input').iCheck({
checkboxClass: 'icheckbox_square-blue'
});
$('.searchinterests input').on('ifChecked', function(event) {
var interestid = event.target.value;
searchSelectedInterests.push(interestid);
updateURLParameters();
performSearch();
});
$('.searchinterests input').on('ifUnchecked', function(event) {
var interestid = event.target.value;
var arrayPos = $.inArray(interestid, searchSelectedInterests);
if (arrayPos > -1) {
searchSelectedInterests.splice(arrayPos, 1);
}
performSearch();
});
$('.searchsdgs input').on('ifChecked', function(event) {
var sdgid = event.target.value;
searchSelectedSdgs.push(sdgid);
updateURLParameters();
performSearch();
});
$('.searchsdgs input').on('ifUnchecked', function(event) {
var sdgid = event.target.value;
var arrayPos = $.inArray(sdgid, searchSelectedSdgs);
if (arrayPos > -1) {
searchSelectedSdgs.splice(arrayPos, 1);
}
performSearch();
});
$('.searchengagements input').on('ifChecked', function(event) {
var social_engagement_id = event.target.value;
searchSelectedEngagements.push(social_engagement_id);
updateURLParameters();
performSearch();
});
$('.searchengagements input').on('ifUnchecked', function(event) {
var social_engagement_id = event.target.value;
var arrayPos = $.inArray(social_engagement_id, searchSelectedEngagements);
if (arrayPos > -1) {
searchSelectedEngagements.splice(arrayPos, 1);
}
performSearch();
});
var searchLocationSelect = $('.stb-search-location');
var radiusSlider = document.getElementById('radius-slider');
var minMaxSlider = document.getElementById('min-max-slider');
var priceSlider = document.getElementById('price-slider');
var daterangepicker = $('#searchdaterange');
var curr_lang = $('#curr_lang').val();
switch(curr_lang) {
case 'nl':
moment.locale('nl');
daterangepicker.daterangepicker({
minDate: moment(),
startDate: moment(),
endDate: moment().add(1, 'years'),
ranges: {
'Volgende week': [moment(), moment().add(1, 'weeks')],
'Volgende maand': [moment(), moment().add(1, 'months')],
'Volgende 3 maanden': [moment(), moment().add(3, 'months')],
'Volgende 6 maanden': [moment(), moment().add(6, 'months')],
'Volgend jaar': [moment(), moment().add(1, 'years')]
},
alwaysShowCalendars: true,
locale: {
"customRangeLabel": "Vrije keuze",
"format": "DD-MM-YYYY"
}
});
break;
case 'en':
moment.locale('en');
daterangepicker.daterangepicker({
minDate: moment(),
startDate: moment(),
endDate: moment().add(1, 'years'),
ranges: {
'Next week': [moment(), moment().add(1, 'weeks')],
'Next month': [moment(), moment().add(1, 'months')],
'Next 3 months': [moment(), moment().add(3, 'months')],
'Next 6 months': [moment(), moment().add(6, 'months')],
'Next year': [moment(), moment().add(1, 'years')]
},
alwaysShowCalendars: true,
locale: {
"customRangeLabel": "Free choice",
"format": "DD-MM-YYYY"
}
});
break;
case 'fr':
moment.locale('fr');
daterangepicker.daterangepicker({
minDate: moment(),
startDate: moment(),
endDate: moment().add(1, 'years'),
ranges: {
'Semaine prochaine': [moment(), moment().add(1, 'weeks')],
'Mois prochain': [moment(), moment().add(1, 'months')],
'3 mois prochains': [moment(), moment().add(3, 'months')],
'6 mois prochains': [moment(), moment().add(6, 'months')],
'L\'année prochaine': [moment(), moment().add(1, 'years')]
},
alwaysShowCalendars: true,
locale: {
"customRangeLabel": "Libre choix",
"format": "DD-MM-YYYY"
}
});
break;
}
daterangepicker.on('hide.daterangepicker', function (ev, picker) {
var startdate = picker.startDate.format('YYYY-MM-DD');
var enddate = picker.endDate.format('YYYY-MM-DD');
setStartDate(startdate);
setEndDate(enddate);
updateURLParameters();
performSearch();
});
if (searchLocationSelect.length) {
searchLocationSelect.selectize({
create: false,
sortField: {
field: 'text',
direction: 'asc'
},
dropdownParent: 'body',
plugins: ['remove_button'],
onChange: function(value) {
setLocation(value);
var size = value.length;
if (size == 1) {
enableRadius(radiusSlider);
} else {
disableAndResetRadius(radiusSlider);
}
updateURLParameters();
performSearch();
}
});
}
noUiSlider.create(radiusSlider, {
start: [0],
step: 5,
range: {
'min': 0,
'max': 100
}
});
var radiusNodes = [
document.getElementById('radius-value')
];
// Display the slider value and how far the handle moved
// from the left edge of the slider.
radiusSlider.noUiSlider.on('update', function (values, handle, unencoded, isTap, positions) {
var value = values[handle];
radiusNodes[handle].innerHTML = Math.round(value);
});
radiusSlider.noUiSlider.on('set', function (value) {
setRadius(value);
updateURLParameters();
performSearch();
});
var minmax_slider_options = {
start: [0,1000],
behaviour: 'drag',
connect: true,
tooltips: [wNumb({
decimals: 0
}), wNumb({
decimals: 0
})],
range: {
'min': 0,
'max': 1000
},
step: 5
};
noUiSlider.create(minMaxSlider, minmax_slider_options);
var minMaxNodes = [
document.getElementById('minmax-lower-value'),
document.getElementById('minmax-upper-value')
];
// Display the slider value and how far the handle moved
// from the left edge of the slider.
minMaxSlider.noUiSlider.on('update', function (values, handle, unencoded, isTap, positions) {
var value = values[handle];
minMaxNodes[handle].innerHTML = Math.round(value);
});
minMaxSlider.noUiSlider.on('set', function (values) {
setMin(values[0]);
setMax(values[1]);
updateURLParameters();
performSearch();
});
var price_slider_options = {
start: [0,50000],
behaviour: 'drag',
connect: true,
tooltips: [wNumb({
decimals: 0
}), wNumb({
decimals: 0
})],
range: {
'min': 0,
'max': 50000
},
step: 250
};
noUiSlider.create(priceSlider, price_slider_options);
var priceNodes = [
document.getElementById('price-lower-value'), // 1000
document.getElementById('price-upper-value') // 3500
];
// Display the slider value and how far the handle moved
// from the left edge of the slider.
priceSlider.noUiSlider.on('update', function (values, handle, unencoded, isTap, positions) {
var value = values[handle];
priceNodes[handle].innerHTML = '€'+Math.round(value);
});
priceSlider.noUiSlider.on('set', function (values) {
setMinPrice(values[0]);
setMaxPrice(values[1]);
updateURLParameters();
performSearch();
});
/** Reset methods **/
$('#resetLocations').on('click', function(e) {
e.preventDefault();
var locationInputField = $('.stb-search-location');
var control = locationInputField[0].selectize;
control.clear();
});
$('#resetRadius').on('click', function(e) {
e.preventDefault();
document.getElementById('radius-slider').noUiSlider.set(0);
});
$('#resetMinMax').on('click', function(e) {
e.preventDefault();
document.getElementById('min-max-slider').noUiSlider.set([0,1000]);
});
$('#resetPrice').on('click', function(e) {
e.preventDefault();
document.getElementById('price-slider').noUiSlider.set([0,50000]);
});
$('#resetInterests').on('click', function(e) {
e.preventDefault();
searchSelectedInterests = [];
$("input[name='interests[]']").iCheck('uncheck');
});
$('#resetSdgs').on('click', function(e) {
e.preventDefault();
searchSelectedSdgs = [];
$("input[name='sdgs[]']").iCheck('uncheck');
});
$('#resetDate').on('click', function(e) {
e.preventDefault();
$('#searchdaterange').data('daterangepicker').setStartDate(moment());
$('#searchdaterange').data('daterangepicker').setEndDate(moment().add(1, 'years'));
var startdate = $('#searchdaterange').data('daterangepicker').startDate.format('YYYY-MM-DD');
var enddate = $('#searchdaterange').data('daterangepicker').endDate.format('YYYY-MM-DD');
setStartDate(startdate);
setEndDate(enddate);
performSearch();
});
$('#resetEngagement').on('click', function(e) {
e.preventDefault();
searchSelectedEngagements = [];
$("input[name='engagement[]']").iCheck('uncheck');
});
// Set initial parameters (and pre-fill the filters based on query params)
setupConfig(radiusSlider);
});
function getQueryStringValue(){
var currentURL = new URI();
var queryParams = currentURL.query(true);
if ($.isEmptyObject(queryParams) === false) {
return queryParams;
} else {
return undefined;
}
}
//In here we read the query parameters from the URL and set the filters to the query parameters (+ do initial filtering)
function setupConfig(radiusSlider) {
var queryParams = getQueryStringValue();
if (queryParams !== undefined) {
var locations = queryParams.locations;
if (locations !== undefined) {
var locationsArray = locations.split(";");
fillLocations(locationsArray);
if (locationsArray.length != 1) {
disableAndResetRadius(radiusSlider);
}
} else {
disableAndResetRadius(radiusSlider);
}
var distance = queryParams.distance;
if (distance !== undefined) {
if (locationsArray.length != 1) {
disableAndResetRadius(radiusSlider);
} else {
document.getElementById('radius-slider').noUiSlider.set(distance);
}
}
var minEmployees = queryParams.minemployees;
var maxEmployees = queryParams.maxemployees;
if ((minEmployees !== undefined) && (maxEmployees !== undefined)) {
document.getElementById('min-max-slider').noUiSlider.set([minEmployees,maxEmployees]);
}
var minPrice = queryParams.minprice;
var maxPrice = queryParams.maxprice;
if ((minPrice !== undefined) && (maxPrice !== undefined)) {
document.getElementById('price-slider').noUiSlider.set([minPrice,maxPrice]);
}
var interests = queryParams.interests;
if (interests !== undefined) {
var interestsArray = interests.split(";");
fillInterests(interestsArray);
}
var sdgs = queryParams.sdgs;
if (sdgs !== undefined) {
var sdgsArray = sdgs.split(";");
fillSdgs(sdgsArray);
}
var startdate = queryParams.startdate;
var enddate = queryParams.enddate;
if ((startdate !== undefined) && (enddate !== undefined)) {
$('#searchdaterange').data('daterangepicker').setStartDate(moment(startdate));
$('#searchdaterange').data('daterangepicker').setEndDate(moment(enddate));
var startdate = $('#searchdaterange').data('daterangepicker').startDate.format('YYYY-MM-DD');
var enddate = $('#searchdaterange').data('daterangepicker').endDate.format('YYYY-MM-DD');
setStartDate(startdate);
setEndDate(enddate);
}
var engagements = queryParams.engagements;
if (engagements !== undefined) {
var engagementsArray = engagements.split(";");
fillEngagements(engagementsArray);
}
} else {
disableAndResetRadius(radiusSlider);
performSearch();
}
}
function fillLocations(locations) {
var selectize = $('.stb-search-location');
selectize[0].selectize.setValue(locations);
}
function fillInterests(interests) {
for (var i = 0; i < interests.length; i++) {
var checkboxId = "interest-"+interests[i];
var checkbox = $('#'+checkboxId);
checkbox.iCheck('check');
}
}
function fillSdgs(sdgs) {
for (var i = 0; i < sdgs.length; i++) {
var checkboxId = "sdg-"+sdgs[i];
var checkbox = $('#'+checkboxId);
checkbox.iCheck('check');
}
}
function fillEngagements(engagements) {
for (var i = 0; i < engagements.length; i++) {
var checkboxId = "eng-"+engagements[i];
var checkbox = $('#'+checkboxId);
checkbox.iCheck('check');
}
}
function getCurrLang() {
return $('#curr_lang').val();
}
function getCurrLangSegment() {
return $('#curr_abbr').val();
}
function getLocation() {
return $('#location').val();
}
function setLocation(value) {
searchLocation = value;
}
function setRadius(value) {
searchRadius = value;
}
function disableAndResetRadius(radiusSlider) {
radiusSlider.noUiSlider.set(0);
radiusSlider.setAttribute('disabled', true);
}
function enableRadius(radiusSlider) {
radiusSlider.removeAttribute('disabled');
}
function setMin(value) {
searchMin = value;
}
function setMax(value) {
searchMax = value;
}
function setMinPrice(value) {
searchMinPrice = value;
}
function setMaxPrice(value) {
searchMaxPrice = value;
}
function setStartDate(value) {
searchStartDate = value;
}
function setEndDate(value) {
searchEndDate = value;
}
function performSearch() {
$('#stb-items-placeholder').html('<div id="loading"><span>'+res.StbSearchPlaceholder+'</span></div>');
var searchOptions = {
type: 'POST',
url: baseUrl + '/dashboard/socialteambuildings/search/getFilteredStbs',
dataType: 'json'
};
var filterdata = {
"curr_lang" : getCurrLang(),
"curr_abbr" : getCurrLangSegment(),
"location" : getLocation(),
"interests": searchSelectedInterests,
"sdgs": searchSelectedSdgs
};
var options = $.extend({}, searchOptions, {
data: filterdata
});
// ajax done & fail
$.ajax(options).done(function (data) {
var mustacheJsonData = data.result;
var html = Mustache.render( $('#stbItemGen').html(), mustacheJsonData );
$('#stb-items-placeholder').html( html );
});
}
function updateURLParameters() {
if (searchLocation != null) {
var locationsUrlString = searchLocation.join(";");
}
var distanceUrlString = Math.round(searchRadius[0]);
var minEmployeesUrlString = Math.round(searchMin);
var maxEmployeesUrlString = Math.round(searchMax);
var minPriceUrlString = Math.round(searchMinPrice);
var maxPriceUrlString = Math.round(searchMaxPrice);
var interestUrlString = searchSelectedInterests.join(";");
var sdgUrlString = searchSelectedSdgs.join(";");
var startDateUrlString = searchStartDate;
var endDateUrlString = searchEndDate;
var engagementUrlString = searchSelectedEngagements.join(";");
var params = {locations: locationsUrlString, distance: distanceUrlString, minemployees: minEmployeesUrlString, maxemployees: maxEmployeesUrlString, minprice: minPriceUrlString, maxprice: maxPriceUrlString, interests: interestUrlString, sdgs: sdgUrlString, startdate: startDateUrlString, enddate: endDateUrlString, engagements: engagementUrlString};
var query = $.param(params);
addURLParameter(query);
}
//This function removes all the query parameters and adds the completely newly built query param string again
function addURLParameter(queryString){
var currentUrl = window.location.href;
var urlNoQueryParams = currentUrl.split("?");
var baseUrl = urlNoQueryParams[0];
var result = baseUrl + "?" + queryString;
window.history.replaceState('', '', result);
}
例如,我尝试在删除选项上使用 e.stopPropagation() 和 e.stopImmediatePropagation()。这不会阻止事件返回到 iCheck 库。
最佳答案
Debounce 在这里不起作用,因为问题不在于一个事件监听器在短时间内有太多请求,而是许多独立的事件监听器一个接一个地触发。
可能的解决方案:
添加一个按钮,例如
SEARCH
,它会实际执行搜索,而不是由个别更新触发。这是解决许多独立监听器问题的好而简单的解决方案。如果您不想添加新按钮,请执行以下操作。使用
setInterval
添加时间间隔以使用 AJAX 执行搜索。并有一个标志是否应该执行搜索。然后,当复选框上的任一监听器更改时,只需将标志设置为true
。此外,如果请求已经在进行中,则在当前请求完成之前不要发出另一个 AJAX 请求。
伪代码如下:
var do_search = false, timer = null, doing_ajax = false, TROTTLE = 500;
timer = setTimeout(performSearch, TROTTLE);
function performSearch()
{
if ( !do_search || doing_ajax )
{
timer = setTimeout(performSearch, TROTTLE);
return;
}
doing_ajax = true;
do_search = false;
// do the ajax request here
// ...
// NOTE: on ajax complete reset the timer, eg timer = setTimeout(performSearch, TROTTLE);
// and set doing_ajax = false;
}
// then your checkboxes listeners will simply update the do-search flag eg:
$('.searchsdgs input').on('ifChecked', function(event) {
var sdgid = event.target.value;
searchSelectedSdgs.push(sdgid);
updateURLParameters();
//performSearch();
do_search = true;
});
$('.searchsdgs input').on('ifUnchecked', function(event) {
var sdgid = event.target.value;
var arrayPos = $.inArray(sdgid, searchSelectedSdgs);
if (arrayPos > -1) {
searchSelectedSdgs.splice(arrayPos, 1);
}
//performSearch();
do_search = true;
});
$('.searchengagements input').on('ifChecked', function(event) {
var social_engagement_id = event.target.value;
searchSelectedEngagements.push(social_engagement_id);
updateURLParameters();
//performSearch();
do_search = true;
});
$('.searchengagements input').on('ifUnchecked', function(event) {
var social_engagement_id = event.target.value;
var arrayPos = $.inArray(social_engagement_id, searchSelectedEngagements);
if (arrayPos > -1) {
searchSelectedEngagements.splice(arrayPos, 1);
}
//performSearch();
do_search = true;
});
注意 您可以调整TROTTLE
间隔以在更直接的交互性和更少的 AJAX 请求之间取得平衡。尝试不同的值以了解您的应用的感受。
注意 2 您可以在此示例的基础上构建一个多反跳函数,例如通过清除超时并在每个单独的监听器中重置它(而不是简单地设置 do_search=true
你可以设置 do_search=true
并清除之前的间隔并重新设置)。这将确保只执行最后一次更新。
关于javascript - 如何通过减少请求量来改进我的 AJAX 实时搜索,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53592232/