5

我正在构建一个日历选择器来伴随(并替换某些浏览器提供的默认日历日期选择器)一个input.type="date"

据记载,为了“简单”,Chrome(可能还有其他浏览器)的日期输入允许输入无效日期,但它们将无法验证(即可以输入 2017 年 6 月 31 日,但会产生“值” ”)。

我的问题是:当输入的日期无效时,如何获取无效日期的值?

var date_input = document.getElementById('date_input');
var output_div = document.getElementById('output_div');

output_div.innerHTML = date_input.value;

date_input.addEventListener('input', function(e){
	output_div.innerHTML = e.target.value;
});
<p>To quickly input an invalid date, select the day field and press the down arrow (on Chrome OSX this displays an empty div below the input instead of the invalid date). This will select 31/06/2017</p>
<input type="date" value="2017-06-01" id="date_input" />
<br />
<br />
<div id="output_div" />

4

1 回答 1

3

不像这样

var date_input = document.getElementById( 'date_input' );
var output_div = document.getElementById( 'output_div' );

output_div.textContent = date_input.valueAsDate; // :-)

date_input.addEventListener( 'input', function( evt ){
    var val = date_input.value,
        sel = document.getSelection();
    if ( date_input.validity.badInput ) {
        // date_input.focus(); // with or without this,
        // date_input.select(); // this doesn't work but,
        // due to the added <span> wrapper:
        sel.selectAllChildren( date_input.parentElement );
        // this does. So:
        val = sel.toString() || function() {
            var copied = document.execCommand( 'copy' );
            sel.empty(); // clear the selection
            return "Invalid date" + ( copied ? " copied to clipboard." : "" );
        }();
    }
    output_div.textContent = val;
});
#output_div {
    margin-top: 1em;
}
<p>To quickly input an invalid date, select the day field and press the down arrow (on Chrome OSX this displays an empty div below the input instead of the invalid date). This will select 31/06/2017</p>
<span><input type="date" value="2017-06-01" id="date_input"></span>
<div id="output_div"></div>

显然,由于value显示的实际上是的影子 DOM项的输出,并且我们无法访问的影子 DOM,因此可见值实际上对 JS 是未知的。 <input>user-agent

出于可访问性的原因,可见值必须可供屏幕阅读器等访问,并且也可以手动复制到剪贴板。

在尝试了多种以编程方式选择和复制文本的方法后,我的想法和耐心都用完了,只能设法将其复制到剪贴板。

  • 如果其他人可以弄清楚如何读取 with JS 的aria-value*属性#shadow-root (user-agent),那么您就有了前进的方向。
  • 如果其他人可以弄清楚如何将selection字符串分配给 a var,那么您还有另一种方法。

然而

由于无效输入仅限于可预测地超出范围,因此您可以监视value在将其更改为无效之前的最后一个有效输入是什么,然后计算它现在肯定是什么。

  • 如果使用日期选择器,所有输入都是有效的。
  • 如果键入,请监视击键。
  • 如果使用箭头按钮,并且日期30/06/2017在使其无效的更改之前,并且、 、29/06/201730/05/2017都有效,则无效日期必须是。30/07/201730/06/201630/06/201831/06/2017

我还没有尝试过让这项工作,但我相信它可以。我会用这个答案发布那个解决方案,但是我的半天时间已经被这个问题吞噬了,我很饿。如果表达了真诚的兴趣,我将很乐意尝试并提供代码。

还是不行

如果它们是使用箭头按钮创建的,并且仅当最后一个日期有效时,这将显示无效日期。我可能会找到解决方法,但正式放弃!生命太短暂了。

根据 MDN

...显示的日期格式将根据用户操作系统的设置区域选择。

这将使跟踪击键变得异常重要。

但是,我刚刚发现<input type="datetime-local">哪个可以解决该问题,并且可以解决其他问题。

所以这做了一些有用的事情:

但是,试图强迫这个工作开始感觉就像试图插入一个通常不适合的西瓜。

这是一个没有魅力甚至没有魅力的笨拙的杂物,如果有人看得太用力,它很可能会破裂。有史以来最好的免责声明;)

var date = "",
    input = document.querySelector( "input" ),
    output = document.querySelector( "output" ),
    bits = function( d ) {
        return [ d.getFullYear(), d.getMonth() + 1, d.getDate() ];
    },
    adjustedDate = function( o ) {
        var d = new Date( date ),
            b = bits( d ),
            year = b[ 0 ],
            month = b[ 1 ],
            day = b[ 2 ];
        switch ( o.i ) {
            case "d": return [ year, month, ( day + o.v ) || 31 ];
            case "m": return [ year, month + o.v, day ];
            case "y": return [ year + o.v, month, day ];
        }
    },
    calcDate = function() {
        var ad, vd, rd;
        [ { i: "d", v: 1 },
          { i: "d", v: -1 },
          { i: "m", v: 1 },
          { i: "m", v: -1 },
          { i: "y", v: 1 },
          { i: "y", v: -1 }
        ].forEach( ( v ) => {
            ad = adjustedDate( v ).join( "-" );
            vd = bits( new Date( ad ) ).join( "-" );
            if ( ad !== vd ) {
                rd = ad;
            }
        } );
        return rd.split( "-" ).map( ( v ) => {
            v = v.toString();
            return v.length === 1 ? "0" + v : v;
        } ).join( "-" ); // tired so mental
    },
    showDate = function() {
        output.textContent = date = ( input.value || calcDate() );
    };
showDate();
input.addEventListener( "input", showDate, false );
output {
    margin-left: 1em;
}
<p>To quickly input an invalid date, select the day field and press the down arrow. This will select 31/06/2017 and output the desired invalid date string.</p>
<form><input type="date" value="2017-06-01"><output></output></form>

于 2017-06-20T10:54:07.397 回答