JQueryDataTables: 延迟搜索,直到输入3个字符或单击一个按钮

是否有一个选项,以开始搜索后,只有3个字符已键入?

我为同事们写了一个 PHP 脚本,显示了20,000个条目,他们抱怨说,当输入一个单词时,前几个字母会导致所有东西都冻结。

另一种方法是通过单击按钮而不是通过字符键入来启动搜索。

下面是我目前的代码:

$("#my_table").dataTable( {
"bJQueryUI": true,
"sPaginationType": "full_numbers",
"bAutoWidth": false,
"aoColumns": [
/* qdatetime */   { "bSearchable": false },
/* id */          null,
/* name */        null,
/* category */    null,
/* appsversion */ null,
/* osversion */   null,
/* details */     { "bVisible": false },
/* devinfo */     { "bVisible": false, "bSortable": false }
],
"oLanguage": {
"sProcessing":   "Wait please...",
"sZeroRecords":  "No ids found.",
"sInfo":         "Ids from _START_ to _END_ of _TOTAL_ total",
"sInfoEmpty":    "Ids from 0 to 0 of 0 total",
"sInfoFiltered": "(filtered from _MAX_ total)",
"sInfoPostFix":  "",
"sSearch":       "Search:",
"sUrl":          "",
"oPaginate": {
"sFirst":    "<<",
"sLast":     ">>",
"sNext":     ">",
"sPrevious": "<"
},
"sLengthMenu": 'Display <select>' +
'<option value="10">10</option>' +
'<option value="20">20</option>' +
'<option value="50">50</option>' +
'<option value="100">100</option>' +
'<option value="-1">all</option>' +
'</select> ids'
}
} );
86961 次浏览

You'll probably have to modify the plugin.

And instead of making it X characters, use a delay, so the search starts once they stopped typing for 1 second or so.

So the keydown/keyup binding which is currently triggering the search would be modified with a timer...

var timer;
clearTimeout(timer);
timer = setTimeout(searchFunctionName, 1000 /* timeToWaitInMS */);

Can you write your own function to test for the length of the inputed string attached to an onKeyUp event handler and trigger the search function once the min length has been reached?

Something along the lines of:

input.onKeyUp(function() {
if(input.length > 3) {
mySearchfunction();
}
});

...that is, in a pseudo code kind of way but you get the jist.

Is there a reason you wouldn't just check length on 'change'?

$('.input').change(function() {
if( $('.input').length > 3 ) {
//do the search
}
});

Note: This was for a much earlier version of data tables, please see this answer for jQuery datatables v1.10 and above.


This will modify the behaviour of the input box to only filter when either return has been pressed or there are at least 3 characters in the search:

$(function(){
var myTable=$('#myTable').dataTable();


$('.dataTables_filter input')
.unbind('keypress keyup')
.bind('keypress keyup', function(e){
if ($(this).val().length < 3 && e.keyCode != 13) return;
myTable.fnFilter($(this).val());
});
});

You can see it working here: http://jsbin.com/umuvu4/2. I don't know why the dataTables folks are binding to both keypress and keyup, but I'm overriding both of them to stay compatible although I think keyup is sufficient.

Hope this helps!

You need to modify the jquery.datatables.js

----- updated ofcourse you can do a check on lenght > 3, but I think you still need a timer. if you have a lot of data, you don't want to keep getting it filtered after every character update.

Within this method:

jqFilter.keyup( function(e) {
if ( **this.value**.length > 3) {
var n = oSettings.aanFeatures.f;
for ( var i=0, iLen=n.length ; i<iLen ; i++ )
{
if ( n[i] != this.parentNode )
{
$('input', n[i]).val( this.value );
}
}
/* Now do the filter */
_fnFilterComplete( oSettings, {
"sSearch": this.value,
"bRegex":  oSettings.oPreviousSearch.bRegex,
"bSmart":  oSettings.oPreviousSearch.bSmart
} );
}
} );

Add a timer to the keyup, like shown in one of the answers.

Then go to this site http://jscompress.com/

And past your modified code and the js wil get minified.

Why not try this extended version of Stony's answer :)

var searchWait = 0;
var searchWaitInterval;
$('.dataTables_filter input')
.unbind('keypress keyup')
.bind('keypress keyup', function(e){
var item = $(this);
searchWait = 0;
if(!searchWaitInterval) searchWaitInterval = setInterval(function(){
if(searchWait>=3){
clearInterval(searchWaitInterval);
searchWaitInterval = '';
searchTerm = $(item).val();
oTable.fnFilter(searchTerm);
searchWait = 0;
}
searchWait++;
},200);


});

This will delay the search until the user has stopped typing.

Hope it helps.

Here's a plugin-like script that extends datatables.

jQuery.fn.dataTableExt.oApi.fnSetFilteringEnterPress = function ( oSettings ) {
var _that = this;


this.each( function ( i ) {
$.fn.dataTableExt.iApiIndex = i;
var
$this = this,
oTimerId = null,
sPreviousSearch = null,
anControl = $( 'input', _that.fnSettings().aanFeatures.f );


anControl
.unbind( 'keyup' )
.bind( 'keyup', function(e) {


if ( anControl.val().length > 2 && e.keyCode == 13){
_that.fnFilter( anControl.val() );
}
});


return this;
} );
return this;
}

usage:

$('#table').dataTable().fnSetFilteringEnterPress();

To invoke the server call after the user has typed the mininum characters in the search box, you can follow Allan's suggestion:

customize the fnSetFilteringDelay() plug-in API function to add an extra condition on the string length before setting the filter, also considering a blank string input to clear the filter

So for a minimum of 3 characters, just change line #19 in the plug-in to:

if ((anControl.val().length == 0 || anControl.val().length >= 3) && (sPreviousSearch === null || sPreviousSearch != anControl.val())) {

Use this

   "fnServerData": function (sSource, aoData, fnCallback, oSettings) {


if ($("#myDataTable_filter input").val() !== "" && $("#myDataTable_filter input").val().length < 3)
return;
oSettings.jqXHR = $.ajax({
"dataType": 'json',
"timeout":12000,
"type": "POST",
"url": sSource,
"data": aoData,
"success": fnCallback
});
}

although it does not answer the original question, i had a complex and slow search on my datatables. the filter event was firing after each keypress, which meant a quite noticeable delay after 10 characters. so by introducing a short delay after a keypress before the filter event was fired, where a subsequent keypress reset the counter and prevented the previous search, i was able to make the search seem much faster. others may find this helpful.

i used the answers from stony and christian noel to make this:

var dataTableFilterTimeout;
var dataTableFilterWait = 200; // number of milliseconds to wait before firing filter


$.fn.dataTableExt.oApi.fnSetFilteringEnterPress = function ( oSettings ) {
var _that = this;
this.each( function ( i ) {
$.fn.dataTableExt.iApiIndex = i;
var $this = this;
var oTimerId = null;
var sPreviousSearch = null;
anControl = $( 'input', _that.fnSettings().aanFeatures.f );
anControl.unbind( 'keyup' ).bind( 'keyup', function(e) {
window.clearTimeout(dataTableFilterTimeout);
if ( anControl.val().length > 2 || e.keyCode == 13){
dataTableFilterTimeout = setTimeout(function(){
_that.fnFilter( anControl.val() );
},dataTableFilterWait);
}
});
return this;
} );
return this;
}

Here is how to handle it with the api change in version 1.10

var searchbox = $('#promogrid_filter input');
var pgrid = $('#promogrid').DataTable();


//Remove default datatable logic tied to these events
searchbox.unbind();


searchbox.bind('input', function (e) {
if(this.value.length >= 3) {
pgrid.search(this.value).draw();
}
if(this.value == '') {
pgrid.search('').draw();
}
return;
});

Solution for version 1.10 -

After looking here for a complete answer and not finding one, I've written this (utilizing code from the documentation, and a few answers here).

The below code works to delay searching until at least 3 characters are entered:

// Call datatables, and return the API to the variable for use in our code
// Binds datatables to all elements with a class of datatable
var dtable = $(".datatable").dataTable().api();


// Grab the datatables input box and alter how it is bound to events
$(".dataTables_filter input")
.unbind() // Unbind previous default bindings
.bind("input", function(e) { // Bind our desired behavior
// If the length is 3 or more characters, or the user pressed ENTER, search
if(this.value.length >= 3 || e.keyCode == 13) {
// Call the API search function
dtable.search(this.value).draw();
}
// Ensure we clear the search if they backspace far enough
if(this.value == "") {
dtable.search("").draw();
}
return;
});

This works on DataTables 1.10.4:

var table = $('#example').DataTable();


$(".dataTables_filter input")
.unbind()
.bind('keyup change', function(e) {
if (e.keyCode == 13 || this.value == "") {
table
.search(this.value)
.draw();
}
});

JSFiddle

You can delay the ajax call to the server by this

var search_thread = null;
$(".dataTables_filter input")
.unbind()
.bind("input", function(e) {
clearTimeout(search_thread);
search_thread = setTimeout(function(){
var dtable = $("#list_table").dataTable().api();
var elem = $(".dataTables_filter input");
return dtable.search($(elem).val()).draw();
}, 300);
});

This code will stop the ajax call if the time between to key press is less then 300 ms, in that way when you write a word, only one ajax call will run and only when you stop typing. You can 'play' with the delay param ( the 300) in order to get more or less delay

You can use the parameter by name minlength in order to restrict the search until 3 characters:

function(request, response) {
$.getJSON("/speakers/autocomplete", {
q: $('#keywordSearch').val()
}, response);
}, minLength: 3

for 1.10 version add this code to your javascript in the options. The initComplete overrides the search method and wait to 3 characters are written. Thanks to http://webteamalpha.com/triggering-datatables-to-search-only-on-enter-key-press/ for giving me the light.

    var dtable= $('#example').DataTable( {
"deferRender": true,
"processing": true,
"serverSide": true,




"ajax": "get_data.php",
"initComplete": function() {
var $searchInput = $('div.dataTables_filter input');


$searchInput.unbind();


$searchInput.bind('keyup', function(e) {
if(this.value.length > 3) {
dtable.search( this.value ).draw();
}
});
}


} );
} );

My version of datatables 1.10.10

I changed a little things and it works now. So, i'm sharing, cause it was difficulty to make it work for version 1.10.10. Thanks to cale_b, Stony and Sam Barnes. Look at the code to see what i did.

    var searchWait = 0;
var searchWaitInterval;
$('.dataTables_filter input')
.unbind() // leave empty here
.bind('input', function(e){ //leave input
var item = $(this);
searchWait = 0;
if(!searchWaitInterval) searchWaitInterval = setInterval(function(){
if(searchWait >= 3){
clearInterval(searchWaitInterval);
searchWaitInterval = '';
searchTerm = $(item).val();
oTable.search(searchTerm).draw(); // change to new api
searchWait = 0;
}
searchWait++;
},200);


});

You can get the length of the data that is being passed in using data.currentTarget.value.length, please see below.

$('[id$="Search"]').keyup(function (data) {
if (data.currentTarget.value.length > 2 || data.currentTarget.value.length == 0) {
if (timoutOut) { clearTimeout(timoutOut); }
timoutOut = setTimeout(function () {
var value = $('[id$="Search"]').val();
$('#jstree').jstree(true).search(value);
}, 250);
}
});

and obviously you would want this code to run when removing text so set the value to 0

Fixed version for datatables 1.10.12 using API and correctly unbinding the 'input'. Also added search clear on backspace under the character limit.

    // Create the Datatable
var pTable = $('#pTable').DataTable();


// Get the Datatable input box and alter events
$('.dataTables_filter input')
.unbind('keypress keyup input')
.bind('keypress keyup input', function (e) {
if ($(this).val().length > 2) {
pTable.search(this.value).draw();
} else if (($(this).val().length == 2) && (e.keyCode == 8)) {
pTable.search('').draw();
}
});

If you're using the old version, it looks like it. Richard's solution works fine. But when I use it, I just added new events, not deleting. Because when code run, table is not yet created. So I found that there is the fnInitComplete method (fire when table created) and I applied it to Ricard's solution. Here it is

$("#my_table").dataTable( {
"bJQueryUI": true,
"sPaginationType": "full_numbers",
"bAutoWidth": false,
...
...,
"fnInitComplete": function (oSettings, json) {
var activeDataTable = $(this).DataTable();
$("#my_table_filter input")
.unbind('keypress keyup')
.bind('keypress keyup', function (e) {


if ($(this).val().length < 3 || e.keyCode !== 13) return;
activeDataTable.fnFilter($(this).val());
});
}

You can use this code on Medtronic datatable or other code to search after using 3 character :

        onDataLoad: function (RequestGrid) {
// execute some code on ajax data load
var searchInput = $('div.dataTables_filter input').val();
if (searchInput.length() > 3 || searchInput.length() ==0) {
alert(searchInput);
dt.draw();
}
else {
return false;
}
},

searchInput.length() ==0 for first show.

This works with DataTables version 1.10.19. It only requires including the js in your website template - useful for a site that has multiple dataTables configured on different pages. Also useful for any slow xhr loading tables, will not allow any new xhr requests until all currently running finish. The search function used is very similar to how the plugin sets up the search function originally.

(function(window, document, $){
var xhring = 0;


$(document).on( 'preXhr.dt', function () {
xhring++;
} );
$(document).on( 'xhr.dt', function () {
xhring--;
} );


//at a minimum wait the full freq, and wait for any pending XHR requests to finish before calling fn
function choke( fn, freq ) {
var
frequency = freq !== undefined ? freq : 200,
last,
timerFn,
timer;


return function () {
var
that = this,
args = arguments;


timerFn = function () {
if (xhring || +new Date() < last + frequency) {
clearTimeout( timer );
timer = setTimeout( timerFn, frequency);
} else {
fn.apply( that, args );
}
}
last = +new Date();


clearTimeout( timer );
timer = setTimeout( timerFn, frequency );
};
}


//See https://github.com/DataTables/DataTables/blob/156faa83386460c578e00c460eca9766e38a0c5f/media/js/jquery.dataTables.js
//See https://github.com/DataTables/Plugins/blob/master/features/searchHighlight/dataTables.searchHighlight.js
$(document).on( 'preInit.dt', function (e, settings, json) {
var previousSearch = settings.oPreviousSearch;


var searchFn = function() {
/* Update all other filter input elements for the new display */
var val = !this.value ? "" : this.value; // mental IE8 fix :-(


/* Now do the filter */
if ( val != previousSearch.sSearch && (val.length >= 3 || val == "")) {
$.fn.dataTable.ext.internal._fnFilterComplete( settings, {
"sSearch": val,
"bRegex": previousSearch.bRegex,
"bSmart": previousSearch.bSmart ,
"bCaseInsensitive": previousSearch.bCaseInsensitive
} );


// Need to redraw, without resorting
settings._iDisplayStart = 0;
$.fn.dataTable.ext.internal._fnDraw( settings );
}
};


var searchDelay = settings.searchDelay !== null ?
settings.searchDelay :
$.fn.dataTable.ext.internal._fnDataSource( settings ) === 'ssp' ?
700 :
200;


var jqFilter = $( 'input', settings.aanFeatures.f )
.off('keyup.DT search.DT input.DT paste.DT cut.DT')
.on('keyup.DT search.DT input.DT paste.DT cut.DT', choke(searchFn, searchDelay))
;
} );


})(window, document, jQuery);

Most of the answers here in some way manipulate the existing DataTable event bindings but personally after spending far too long trying to get this working the best approach I found, in the end, was to just send a dummy value in the search parameter during the ajax call.

// ... denotes expected code for DataTable to function excluded for clarity.
$("#example").dataTable({
...
'ajax': {
...
'data': function (d) {
d.search.value = d.search.value.length >= 3 ? d.search.value : "##EmptySearch##";
return JSON.stringify(d);
}
}
});

The ##EmptySearch## string simply acts as a value that shouldn't match any returned data (it's entirely personal preference what to use, but it should be a string that is guaranteed to not match any data). Because the bindings have not been manipulated all the usual bells and whistles still work but nothing meaningful is returned until the search is greater than or equal to three characters. Admittedly it isn't ideal, would prefer to not make the server request at all but this is (in my opinion) the simplest approach that doesn't ruin the existing functionality of the DataTable search.

None of the previous solutions worked for me so I have made this modified version that also adds a debounce. Working perfectly with all the latest versions.

You can simply change or remove the min character limit and debounce timeout value.

jQuery(document).on( 'init.dt', function (e, settings) {
var dti = jQuery('.dataTables_filter input');
var api = new jQuery.fn.dataTable.Api( settings );
var dbn = null;


dti.off().on('input', function(e) {
clearTimeout( dbn );
var str = this.value;
dbn = setTimeout(function(){
if(str.length > 2 || e.keyCode == 13) api.search( str ).draw();
if(str == '') api.search( '' ).draw();
}, 300);
return;
});
});