延迟加载自动完成数据列表 angularjs
Lazy loading autocomplete datalist angularjs
我有一份工作要重构一些代码以提高对 IE 11
的偏好,
所需的改进之一是为自动完成输入做一些延迟加载,因为 IE 11
渲染 array
有 600 个项目时有点重。
我在一些限制下工作:
1. I have 4 hours to done the job (it's mean that there is no time to massive changes)
2. AngularJs version 1.3.1
3. jQuery version 1.6.8
4. The page is hosting on salesforce (it's not changing anything because it's run html and angularjs).
这是一个 GIF
问题:
link to GIF
我成功地进行了一些延迟加载,但由于某些我不知道如何解释的原因,它无法正常工作。
只有在我失去焦点并再次关注输入字段后才会自动完成渲染列表。
这是HTML代码:
<input type="text" id="ddProductAutoCompleteInput" name="product" class="selectBoxTom" data-ng-model="item.onlineProd" datalist="t_productsList_DDP" placeholder="All Products" />
<datalist id="t_productsList_DDP" size="5" style="overflow-y: scroll">
<option ng-repeat="prod in productList | limitTo:totalLimit track by $index" value="{{prod.Tapi_Online_Product_Name__c}}" ngc-done="'scope.setAutoCompletProdectDwonloadDoc'"/>
</datalist>
这里是 AngularJs 代码:
$scope.setAutoCompletProdectDwonloadDoc = function()//sourceList)
{
var flag = true;
var elem = jQuery("#ddProductAutoCompleteInput");
var list = jQuery("#t_productsList_DDP");
$scope.totalLimit = 50;
jQuery('.ui-autocomplete').scroll(function(){
if (jQuery(this).scrollTop() + jQuery(this).innerHeight() >= jQuery(this)[0].scrollHeight) {
$scope.totalLimit += 10;
elem.autocomplete.focusout();
}
})
elem.autocomplete({
source: list.children().map(function(){return jQuery(this).val();}).get(),//sourceList,
minLength: 0,
search: function(event,ui){
jQuery(this).data("autocomplete").menu.bindings = $();
},
select: function (event, ui)
{
var valueP = ui.item.value;
flag = false;
elem.autocomplete('close');
$scope.$apply(function()
{
$scope.item.onlineProd = valueP;
angular.forEach($scope.docTypeList, function(docType){
docType.selected = false});
$scope.tempList.length = 0;
$scope.filterd = 'false';
});
},
open:function (event, ui)
{
// Try to select the first one if it's the only one
var $children = jQuery(this).data('autocomplete').menu.element.children();
if ($children.size() == 1)
{
}/*else{
var item = '';
var childs = $children.toArray();
for(var i = 0; i< $children.length; i++){
if(event.target.value.toLowerCase().trim() === childs[i].innerText.toLowerCase().trim()){
item = $children.eq(i);
}
}
if(item !== '')
jQuery(this).data('autocomplete').menu.focus(null, item);
}*/
}
}).focus(function(){
if (flag)
{
jQuery(this).autocomplete('search',jQuery(this).val())
flag = false;
}else{
elem.autocomplete('close');
flag = true;
return false;
}
}).focusout(function(){flag = true; return false;})
$scope.totalLimit = 50;
};
我只是延迟加载会正常工作 - 这意味着当我向下滚动列表时 - 还有 10 个项目将呈现到列表中。enter link description here
经过深入研究,我找到了解决方案
解决方案的参考是
http://jsfiddle.net/LesignButure/
所以我的问题的主要问题是我必须处理现有代码而不是更改它,只需在上面添加一些脚本。
页面尚未呈现的假设是错误的,因为自动完成的生命周期是 -
1. totalLimit 设置列表限制
2. setAutoCompletProdectDwonloadDoc被激活并配置autocomplete的结构
3.列表呈现
4. 向下滚动后 - totalLimit 发生变化,组件已呈现,但列表未更改,因为自动完成的 'open' 功能尚未 运行。
5. 重新点击输入框后显示新列表
所以我所做的更改在这里:
/*product*/
$scope.setAutoCompletProdectDwonloadDoc = function()//sourceList)
{
var flag = true;
var elem = jQuery("#ddProductAutoCompleteInput");
var list = jQuery("#t_productsList_DDP");
/**
elem.autocomplete({
source: list.children().map(function () { return jQuery(this).val(); }).get(),//sourceList,
minLength: 0,
search: function (event, ui) {
jQuery(this).data("autocomplete").menu.bindings = $();
},
select: function (event, ui) {
var valueP = ui.item.value;
flag = false;
elem.autocomplete('close');
$scope.$apply(function () {
$scope.item.onlineProd = valueP;
angular.forEach($scope.docTypeList, function (docType) {
docType.selected = false
});
$scope.tempList.length = 0;
$scope.filterd = 'false';
});
},
open: function (event, ui) {
var $children = jQuery(this).data('autocomplete').menu.element.children();
}
}).focus(function () {
if (flag) {
jQuery(this).autocomplete('search', jQuery(this).val())
flag = false;
} else {
elem.autocomplete('close');
flag = true;
return false;
}
}).focusout(function () { flag = true; return false; })
**/
elem.autocomplete({
source: list.children().map(function () { return jQuery(this).val(); }).get(),
minLength: 0,
delay: 0,
search: function (event, ui) {
jQuery(this).data("autocomplete").menu.bindings = $();
},
select: function (event, ui) {
var valueP = ui.item.value;
flag = false;
elem.autocomplete('close');
$scope.$apply(function () {
$scope.item.onlineProd = valueP;
angular.forEach($scope.docTypeList, function (docType) {
docType.selected = false
});
$scope.tempList.length = 0;
$scope.filterd = 'false';
});
},
open: function (event, ui) {
var $children = jQuery(this).data('autocomplete').menu.element.children();
}
}).focus(function () {
//reset result list's pageindex when focus on
window.pageIndex = 0;
$(this).autocomplete("search");
}).focusout(function () { flag = true; return false; });
$.extend($.ui.autocomplete.prototype, {
_renderMenu: function (ul, items) {
//remove scroll event to prevent attaching multiple scroll events to one container element
$(ul).unbind("scroll");
var self = this;
self._scrollMenu(ul, items);
},
_scrollMenu: function (ul, items) {
var maxShow = 999;
if(items.length > 400)
maxShow = 20;
var self = this;
var results = [];
var pages = Math.ceil(items.length / maxShow);
results = items.slice(0, maxShow);
if (pages > 1) {
$(ul).scroll(function () {
if (isScrollbarBottom($(ul))) {
++window.pageIndex;
if (window.pageIndex >= pages) return;
results = items.slice(window.pageIndex * maxShow, window.pageIndex * maxShow + maxShow);
//append item to ul
$.each(results, function (index, item) {
self._renderItem(ul, item);
});
//refresh menu
self.menu.deactivate();
self.menu.refresh();
// size and position menu
ul.show();
self._resizeMenu();
ul.position($.extend({
of: self.element
}, self.options.position));
if (self.options.autoFocus) {
self.menu.next(new $.Event("mouseover"));
}
}
});
}
$.each(results, function (index, item) {
self._renderItem(ul, item);
});
}
});
function isScrollbarBottom(container) {
var height = container.outerHeight();
var scrollHeight = container[0].scrollHeight;
var scrollTop = container.scrollTop();
if (scrollTop >= scrollHeight - height) {
return true;
}
return false;
};
};
我有一份工作要重构一些代码以提高对 IE 11
的偏好,
所需的改进之一是为自动完成输入做一些延迟加载,因为 IE 11
渲染 array
有 600 个项目时有点重。
我在一些限制下工作:
1. I have 4 hours to done the job (it's mean that there is no time to massive changes)
2. AngularJs version 1.3.1
3. jQuery version 1.6.8
4. The page is hosting on salesforce (it's not changing anything because it's run html and angularjs).
这是一个 GIF
问题:
link to GIF
我成功地进行了一些延迟加载,但由于某些我不知道如何解释的原因,它无法正常工作。 只有在我失去焦点并再次关注输入字段后才会自动完成渲染列表。
这是HTML代码:
<input type="text" id="ddProductAutoCompleteInput" name="product" class="selectBoxTom" data-ng-model="item.onlineProd" datalist="t_productsList_DDP" placeholder="All Products" />
<datalist id="t_productsList_DDP" size="5" style="overflow-y: scroll">
<option ng-repeat="prod in productList | limitTo:totalLimit track by $index" value="{{prod.Tapi_Online_Product_Name__c}}" ngc-done="'scope.setAutoCompletProdectDwonloadDoc'"/>
</datalist>
这里是 AngularJs 代码:
$scope.setAutoCompletProdectDwonloadDoc = function()//sourceList)
{
var flag = true;
var elem = jQuery("#ddProductAutoCompleteInput");
var list = jQuery("#t_productsList_DDP");
$scope.totalLimit = 50;
jQuery('.ui-autocomplete').scroll(function(){
if (jQuery(this).scrollTop() + jQuery(this).innerHeight() >= jQuery(this)[0].scrollHeight) {
$scope.totalLimit += 10;
elem.autocomplete.focusout();
}
})
elem.autocomplete({
source: list.children().map(function(){return jQuery(this).val();}).get(),//sourceList,
minLength: 0,
search: function(event,ui){
jQuery(this).data("autocomplete").menu.bindings = $();
},
select: function (event, ui)
{
var valueP = ui.item.value;
flag = false;
elem.autocomplete('close');
$scope.$apply(function()
{
$scope.item.onlineProd = valueP;
angular.forEach($scope.docTypeList, function(docType){
docType.selected = false});
$scope.tempList.length = 0;
$scope.filterd = 'false';
});
},
open:function (event, ui)
{
// Try to select the first one if it's the only one
var $children = jQuery(this).data('autocomplete').menu.element.children();
if ($children.size() == 1)
{
}/*else{
var item = '';
var childs = $children.toArray();
for(var i = 0; i< $children.length; i++){
if(event.target.value.toLowerCase().trim() === childs[i].innerText.toLowerCase().trim()){
item = $children.eq(i);
}
}
if(item !== '')
jQuery(this).data('autocomplete').menu.focus(null, item);
}*/
}
}).focus(function(){
if (flag)
{
jQuery(this).autocomplete('search',jQuery(this).val())
flag = false;
}else{
elem.autocomplete('close');
flag = true;
return false;
}
}).focusout(function(){flag = true; return false;})
$scope.totalLimit = 50;
};
我只是延迟加载会正常工作 - 这意味着当我向下滚动列表时 - 还有 10 个项目将呈现到列表中。enter link description here
经过深入研究,我找到了解决方案 解决方案的参考是 http://jsfiddle.net/LesignButure/
所以我的问题的主要问题是我必须处理现有代码而不是更改它,只需在上面添加一些脚本。
页面尚未呈现的假设是错误的,因为自动完成的生命周期是 - 1. totalLimit 设置列表限制 2. setAutoCompletProdectDwonloadDoc被激活并配置autocomplete的结构 3.列表呈现 4. 向下滚动后 - totalLimit 发生变化,组件已呈现,但列表未更改,因为自动完成的 'open' 功能尚未 运行。 5. 重新点击输入框后显示新列表
所以我所做的更改在这里:
/*product*/
$scope.setAutoCompletProdectDwonloadDoc = function()//sourceList)
{
var flag = true;
var elem = jQuery("#ddProductAutoCompleteInput");
var list = jQuery("#t_productsList_DDP");
/**
elem.autocomplete({
source: list.children().map(function () { return jQuery(this).val(); }).get(),//sourceList,
minLength: 0,
search: function (event, ui) {
jQuery(this).data("autocomplete").menu.bindings = $();
},
select: function (event, ui) {
var valueP = ui.item.value;
flag = false;
elem.autocomplete('close');
$scope.$apply(function () {
$scope.item.onlineProd = valueP;
angular.forEach($scope.docTypeList, function (docType) {
docType.selected = false
});
$scope.tempList.length = 0;
$scope.filterd = 'false';
});
},
open: function (event, ui) {
var $children = jQuery(this).data('autocomplete').menu.element.children();
}
}).focus(function () {
if (flag) {
jQuery(this).autocomplete('search', jQuery(this).val())
flag = false;
} else {
elem.autocomplete('close');
flag = true;
return false;
}
}).focusout(function () { flag = true; return false; })
**/
elem.autocomplete({
source: list.children().map(function () { return jQuery(this).val(); }).get(),
minLength: 0,
delay: 0,
search: function (event, ui) {
jQuery(this).data("autocomplete").menu.bindings = $();
},
select: function (event, ui) {
var valueP = ui.item.value;
flag = false;
elem.autocomplete('close');
$scope.$apply(function () {
$scope.item.onlineProd = valueP;
angular.forEach($scope.docTypeList, function (docType) {
docType.selected = false
});
$scope.tempList.length = 0;
$scope.filterd = 'false';
});
},
open: function (event, ui) {
var $children = jQuery(this).data('autocomplete').menu.element.children();
}
}).focus(function () {
//reset result list's pageindex when focus on
window.pageIndex = 0;
$(this).autocomplete("search");
}).focusout(function () { flag = true; return false; });
$.extend($.ui.autocomplete.prototype, {
_renderMenu: function (ul, items) {
//remove scroll event to prevent attaching multiple scroll events to one container element
$(ul).unbind("scroll");
var self = this;
self._scrollMenu(ul, items);
},
_scrollMenu: function (ul, items) {
var maxShow = 999;
if(items.length > 400)
maxShow = 20;
var self = this;
var results = [];
var pages = Math.ceil(items.length / maxShow);
results = items.slice(0, maxShow);
if (pages > 1) {
$(ul).scroll(function () {
if (isScrollbarBottom($(ul))) {
++window.pageIndex;
if (window.pageIndex >= pages) return;
results = items.slice(window.pageIndex * maxShow, window.pageIndex * maxShow + maxShow);
//append item to ul
$.each(results, function (index, item) {
self._renderItem(ul, item);
});
//refresh menu
self.menu.deactivate();
self.menu.refresh();
// size and position menu
ul.show();
self._resizeMenu();
ul.position($.extend({
of: self.element
}, self.options.position));
if (self.options.autoFocus) {
self.menu.next(new $.Event("mouseover"));
}
}
});
}
$.each(results, function (index, item) {
self._renderItem(ul, item);
});
}
});
function isScrollbarBottom(container) {
var height = container.outerHeight();
var scrollHeight = container[0].scrollHeight;
var scrollTop = container.scrollTop();
if (scrollTop >= scrollHeight - height) {
return true;
}
return false;
};
};