javascript 我的 DataTables 日期范围过滤器使用 jQuery UI Datepicker 仅适用于 Google Chrome

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22080132/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 22:23:22  来源:igfitidea点击:

My DataTables date range filter using jQuery UI Datepicker works only in Google Chrome

javascriptjqueryjquery-uidatatablesjquery-datatables

提问by DrewT

I feel like I could use another pair of eyes on my code right now.

我觉得我现在可以用另一双眼睛看我的代码了。

I am using jQuery UI's Datepicker to grab dates from two html inputs:

我正在使用 jQuery UI 的 Datepicker 从两个 html 输入中获取日期:

<!-- HTML inputs -->
<p id="date_filter">
    <span id="date-label-from" class="date-label"><?php echo LANG_FROMDATE; ?>: </span><input class="date_range_filter date" type="text" id="datepicker_min" /><img id="calender-from" class="datepicker-calender" src="includes/js/jquery/jquery-ui/img/calendar.png" width="17px" height="18px" />
    <span id="date-label-to" class="date-label"><?php echo LANG_TODATE; ?>: </span><input class="date_range_filter date" type="text" id="datepicker_max" /><img id="calender-to" class="datepicker-calender" src="includes/js/jquery/jquery-ui/img/calendar.png" width="17px" height="18px" />
    <button class="btn" id="reset_btn"><?php echo LANG_RESET; ?></button>
</p>

That calender img tag is just an icon that also triggers the datepicker, i.e. like this:

那个日历 img 标签只是一个也会触发日期选择器的图标,例如:

$(document).ready(function() {
    $('#calender-from').click(function() {
        $("#datepicker_min").datepicker("show");
    });
    $('#calender-to').click(function() {
        $("#datepicker_max").datepicker("show");
    });
});

So that's the basic stuff that's all good. The issue I'm having is I have written javascript that is an extension to the DataTables filtering api. I wrote my code based on this example: https://datatables.net/examples/plug-ins/range_filtering.html

所以这是基本的东西,一切都很好。我遇到的问题是我编写了 javascript,它是 DataTables 过滤 api 的扩展。我根据这个例子编写了我的代码:https: //datatables.net/examples/plug-ins/range_filtering.html

The problem is right now my code is only working in Google Chrome and fails in FireFox, Safari, and Internet Explorer. I probably am just missing something small or have some minor semantic mistake in my code.

问题是现在我的代码只能在 Google Chrome 中运行,而在 FireFox、Safari 和 Internet Explorer 中失败。我可能只是遗漏了一些小东西,或者我的代码中有一些轻微的语义错误。

I extend the DataTables filtering API like this:

我像这样扩展 DataTables 过滤 API:

// Date range filter
var minDateFilter = "";
var maxDateFilter = "";

$.fn.dataTableExt.afnFiltering.push(
    function( oSettings, aData, iDataIndex ) {
        if ( typeof aData._date == 'undefined' ) {
            aData._date = new Date(aData[0]).getTime();
        }

        if ( minDateFilter && !isNaN(minDateFilter) ) {
            if ( aData._date < minDateFilter ) {
                return false;
            }
        }

        if ( maxDateFilter && !isNaN(maxDateFilter) ) {
            if ( aData._date > maxDateFilter ) {
                return false;
            }
        }

        return true;
    }
);

This way seems to return false in other browsers because I will get a result of "Showing 1 - 50 of 67 records" regardless of what data I have in the datapicker inputs.

这种方式似乎在其他浏览器中返回 false,因为无论我在数据选择器输入中有什么数据,我都会得到“显示 1 - 67 条记录中的 50 条”的结果。

I'm handling the jQuery UI Datepicker inputs like this:

我正在处理这样的 jQuery UI Datepicker 输入:

$(document).ready(function() {
    $("#datepicker_min").datepicker({
        "onSelect": function(date) {
            minDateFilter = new Date(date).getTime();
            oTable.fnDraw();
        }
    }).keyup(function(){
        minDateFilter = new Date(this.value).getTime();
        oTable.fnDraw();
    });

    $( "#datepicker_max" ).datepicker( {
        "onSelect": function(date) {
            maxDateFilter = new Date(date).getTime();
            oTable.fnDraw();
        }
    }).keyup(function(){
        maxDateFilter = new Date(this.value).getTime();
        oTable.fnDraw();
    });
});

I also tried extending the filter API like this (to be closer to original DataTables example):

我还尝试像这样扩展过滤器 API(更接近原始 DataTables 示例):

$.fn.dataTableExt.afnFiltering.push(
    function( oSettings, aData, iDataIndex ) {
        var iMin = minDateFilter;
        var iMax = maxDateFilter;
        var iDate = new Date(aData[0]).getTime();
        if ( iMin == "" && iMax == "" )
        {
            return true;
        }
        else if ( iMin == "" && iDate < iMax )
        {
            console.log("iDate 1 = "+iDate);
            return true;
        }
        else if ( iMin < iDate && "" == iMax )
        {
            console.log("iDate 2 = "+iDate);
            return true;
        }
        else if ( iMin < iDate && iDate < iMax )
        {
            console.log("iDate = 3 "+iDate);
            return true;
        }
        return false;
    }
);

When I do it this way I get a somewhat similar result of my filter code working correctly only in Chrome, and in the other browsers I'll get a result of "Showing 0 to 0 records (filtered from 67 total records)" so here it returns true but it filters incorrectly - i.e. it shows 0 results after filtering regardless of what values are in the datepicker inputs.

当我这样做时,我的过滤器代码只能在 Chrome 中正常工作,结果有点类似,而在其他浏览器中,我将得到“显示 0 到 0 条记录(从 67 条总记录中过滤出来)”的结果,所以在这里它返回 true 但它过滤不正确 - 即它在过滤后显示 0 结果,而不管日期选择器输入中的值是什么。

Ugh! why is my code only working in Google Chrome and not working in other browsers (Safari, IE, FireFox)?? Any help is appreciated, thanks in advance!

啊! 为什么我的代码只能在 Google Chrome 中运行,而不能在其他浏览器(Safari、IE、FireFox)中运行??任何帮助表示赞赏,提前致谢!

采纳答案by DrewT

After all this time I actually finally found out what causes this issue.

经过这么长时间,我终于找到了导致此问题的原因。

Turns out it is an issue with javascript date time objects being parseddifferently in different browsers.

原来这是在不同浏览器中解析javascript 日期时间对象的问题

So, in order to make your filters fully cross browser friendly, you just need to use date format in your jQuery UI datepicker that works for all broswers to parse dates from using JS new Date(date_var). But that's easier said than done. What I do is manipulate the date time value before sending it through the custom Datatables filter.

因此,为了使您的过滤器完全跨浏览器友好,您只需要在适用于所有浏览器的 jQuery UI 日期选择器中使用日期格式来解析使用 JS 的日期new Date(date_var)。但这说起来容易做起来难。我所做的是在通过自定义数据表过滤器发送日期时间值之前对其进行操作。

See below for the most recent cross browser implementation I've made. It's overall a better implementation than what is in my question because you don't need to worry as much about the filtering api extension it is handled automatically by the function. See the comments in the code itself for an example where I manipulate the date format to work nicely in Safari'sdate.parse(dateString)syntax.

有关我所做的最新跨浏览器实现,请参见下文。总的来说,它比我的问题中的实现更好,因为您不必担心由函数自动处理的过滤 api 扩展。请参阅代码本身的注释以获取示例,其中我操纵日期格式以在 Safari 的date.parse(dateString)语法中很好地工作

$(document).ready( function () {
    // define vars
    var dTable = $('#transactions'),
        date_from = null,
        date_to = null,
        dateRangeFromSearchBox = $('#date_from_range_search'),
        dateRangeToSearchBox = $('#date_to_range_search'),
        dateRangeResetButton = $('#date_range_reset_button'),

    // bootstrap our datatable
    dTable.DataTable();

    // bootstrap datepickers
    $('input.datepicker').datepicker({
        dateFormat: 'yy-mm-dd',
        onSelect: function (date) {
            date_from = new Date( dateRangeFromSearchBox.val() ).getTime();
            date_to = new Date( dateRangeToSearchBox.val() ).getTime();
            // force change event
            dateRangeFromSearchBox.trigger('change');
            dateRangeToSearchBox.trigger('change');
            // disable further if filtering occured
            if (!isNaN(date_from) &&
                !isNaN(date_from) &&
                dateRangeFromSearchBox.val().length > 0 &&
                dateRangeToSearchBox.val().length > 0) {
                    dateRangeFromSearchBox.prop('disabled', true);
                    dateRangeToSearchBox.prop('disabled', true);
            }
        }
    });

    // date range search
    // date from
    dateRangeFromSearchBox.change( function () {
        if (date_from === "" ||
            date_to === "" ||
            dateRangeFromSearchBox.val().length === 0 ||
            dateRangeToSearchBox.val().length === 0) {
                //console.log('reset event from box');
                dTable_filterColumnByDateRange (dTable, 1, '', '');
        // exit asap if date is invalid
        } else if ( isNaN(date_from) || isNaN(date_to) ) {
            //console.log('nan event from box')
            return;
        } else {
            //console.log('filter event from box');
            // we good? let's filter
            dTable_filterColumnByDateRange (dTable, 1, date_from, date_to);
        }
    });

    // date to
    dateRangeToSearchBox.change( function () {
        if (date_from === "" ||
            date_to === "" ||
            dateRangeFromSearchBox.val().length === 0 ||
            dateRangeToSearchBox.val().length === 0) {
                //console.log('reset event to box');
                dTable_filterColumnByDateRange (dTable, 1, '', '');
        // exit asap if date is invalid
        } else if ( isNaN( new Date( dateRangeToSearchBox.val() ).getTime() ) || isNaN(date_from) ) {
            //console.log('nan event to box')
            return;
        } else {
            //console.log('filter event from box');
            // we good? let's filter
            dTable_filterColumnByDateRange (dTable, 1, date_from, date_to);
        }
    });

    //date range reset
    dateRangeResetButton.click( function () {
        //console.log('reset event');
        // this part is crazy
        // don't worry I know it is
        $('#date_from_range_search').val('').promise().done( function () {
            $(this)
                .trigger('change')
                .prop('disabled', false);
            $('#date_to_range_search').val('').promise().done( function () {
                $(this)
                    .trigger('change')
                    .prop('disabled', false);
            });
        });
    });
});

// and now here is our worker function
// which the above dom ready code is calling:
/**
 * Filters a single column based on two date range values
 * @param {Object} data_table - jQuery html object instance of the table
 * @param {Integer} column_index - DataTables int value of date column to filter
 * @param {Integer} date_from - unix time stamp of start date to filter between
 * @param {Integer} date_to - unix time stamp of end date to filter between
 */
dTable_filterColumnByDateRange = function (data_table, column_index, date_from, date_to) {
    var rowValue_asTimeStamp = null;

    data_table
        .DataTable()
        .column()
        .data()
        .filter( function (value, index) {
            rowValue_asTimeStamp = new Date(value).getTime();

            // debug:
            //console.log('from', date_from);
            //console.log('to', date_to);
            //console.log('row', rowValue_asTimeStamp);
            //console.log('existing filters?', $.fn.dataTableExt.afnFiltering.length);
            if (date_to == null ||
                date_to == "" ||
                date_from == null ||
                date_from == "") {
                    $.fn.dataTableExt.afnFiltering.pop();
                    var returnVal = true;
            } else {
                var returnVal = (rowValue_asTimeStamp >= date_from && rowValue_asTimeStamp <= date_to) ? true : false;
            }
        })
        .draw();

        // extend the filter API in the
        // most annoying way possible
        $.fn.dataTableExt.afnFiltering.push(
            function (oSettings, aData, iDataIndex) {
                // built in reset?
                if (date_to == null ||
                    date_to == "" ||
                    date_from == null ||
                    date_from == "") {
                        //console.log(aData[column_index]);
                        return true;
                } else {
                    // debug:
                    //console.log('arg 1', (new Date(aData[column_index])).getTime());
                    //console.log('date from', date_from);
                    //console.log('date to', date_to);
                    //console.log('truth condition', ((new Date(aData[column_index])).getTime() > date_from && (new Date(aData[column_index])).getTime() < date_to));
                    /**
                     * THIS PART MAKES THE FILTER WORK IN SAFARI :)
                     */
                    var parsed = aData[column_index].replace(' ', 'T');
                    return ((new Date( parsed )).getTime() > date_from && (new Date( parsed )).getTime() < date_to);
                }
            }
        );
};

The above example is intended to work for columns that use a timestamp column value like: <td>2016-08-05 19:14:00</td>

上面的示例适用于使用时间戳列值的列,例如: <td>2016-08-05 19:14:00</td>

回答by mainguy

I have created a plunker from your script and added some made up html table. I used as much of your code as possible but had to adjust some of your ids. I'm not sure where along the way i made changes that finally got this working, but I guess its mainly related to your filter variables being not global.

我从你的脚本中创建了一个 plunker 并添加了一些组成的 html 表。我尽可能多地使用了你的代码,但不得不调整你的一些 id。我不确定在此过程中我在哪里进行了更改,最终使此工作正常进行,但我想这主要与您的过滤器变量不是全局的有关。

// Date range filter
minDateFilter = "";
maxDateFilter = "";

instead of:

代替:

// Date range filter
var minDateFilter = "";
var maxDateFilter = "";

Have a look at this Plunkerwith the full code which runs perfectly on FF 27.0.1 and find out for yourself.

看看这个Plunker的完整代码,它在 FF 27.0.1 上完美运行,然后自己找出来。

Update: Simplified the datepicker initialisition to this:

更新:将日期选择器初始化简化为:

  $("#datepicker_from").datepicker({
    showOn: "button",
    buttonImage: "images/calendar.gif",
    buttonImageOnly: false,
    "onSelect": function(date) {
      minDateFilter = new Date(date).getTime();
      oTable.fnDraw();
    }
  }).keyup(function() {
    minDateFilter = new Date(this.value).getTime();
    oTable.fnDraw();
  });

This prevents the double assignement of the widget you have done in:

这可以防止您在以下位置完成的小部件的双重分配:

    $('#calender-from').click(function() {
        $("#datepicker_min").datepicker("show");
    });

and:

和:

    $("#datepicker_min").datepicker({
        "onSelect": function(date) {
            minDateFilter = new Date(date).getTime();
            oTable.fnDraw();
        }

Now it uses its native function to diplay an icon.

现在它使用其本机功能来显示图标。

Maybe some browser are picky about this.

也许某些浏览器对此很挑剔。

Updated Plunker

更新的Plunker

回答by user2793022

Here is the script that worked for me.

这是对我有用的脚本。

$.fn.dataTableExt.afnFiltering.push(
    function(oSettings, aData, iDataIndex){
        var dateStart = parseDateValue($("#dateStart").val());
        var dateEnd = parseDateValue($("#dateEnd").val());


        var evalDate= parseDateValue(aData[5]);
        var evalDate1= parseDateValue(aData[6]);

        if ((evalDate >= dateStart && evalDate <= dateEnd)||(evalDate1 >= dateStart && evalDate1 <= dateEnd)) {
            return true;
        }
        else {
            return false;
        }

    });`