使用 JS 实现粘贴到 <input type="date" />
不知道从哪个版本的 Chrome 或 Edge 开始,我们无法通过 ctrl+v 快捷键将时间格式的字符串粘贴到 type 为 date 的 input 框中,我们想办法用 JS 来实现。
方式一、监听 paste 事件:
const input = document.querySelector('input[type="date"]');
input.addEventListener('paste', (event) => {
input.value = event.clipboardData.getData('text');
});
这段代码实现了从页面获取这个 input 元素,监听它的 paste 事件,然后将粘贴板的文本内容赋值给 input。
经测试,当焦点在“年”的位置时可以粘贴成功,但焦点在“月”或“日”上不会触发 paste 事件。
方式二、监听 keydown 事件:
const input = document.querySelector('input[type="date"]');
input.addEventListener('keydown', (event) => {
if ((navigator.platform.match("Mac") ? event.metaKey : event.ctrlKey) && event.key === 'v') {
event.preventDefault();
var clipboardData = (event.clipboardData || event.originalEvent.clipboardData);
input.value = clipboardData.getData('text');
}
});
测试发现报错误:
Uncaught TypeError: Cannot read properties of undefined (reading 'getData')
Uncaught TypeError: Cannot read properties of undefined (reading 'clipboardData')
看来 event 中没有 clipboardData 对象,改为从 window.navigator 获取:
const input = document.querySelector('input[type="date"]');
input.addEventListener('keydown', (event) => {
if ((navigator.platform.match("Mac") ? event.metaKey : event.ctrlKey) && event.key === 'v') {
event.preventDefault();
window.navigator.clipboard.readText().then(text => {
input.value = text;
});
}
});
缺点是需要用户授权:
仅第一次需要授权,如果用户拒绝,那么以后就默认拒绝了。
以上两种方式各有优缺点,选择一种适合你的方案就行。接下来继续完善。
兼容更多时间格式,并调整时区
<input type="date" /> 默认的日期格式是 yyyy-MM-dd,如果要兼容 yyyy-M-d 等格式,那么:
const parsedDate = new Date(text);
if (!isNaN(parsedDate.getTime())) {
input.value = parsedDate.toLocaleDateString('en-GB', { year: 'numeric', month: '2-digit', day: '2-digit' }).split('/').reverse().join('-');
}
以 text 为“2023-4-20”举例,先转为 Date,如果成功,再转为英国时间格式“20-04-2023”,以“/”分隔,逆序,再以“-”连接,就变成了“2023-04-20”。
当然如果希望支持中文的年月日,可以先用正则表达式替换一下:
text = text.replace(/\s*(\d{4})\s*年\s*(\d{1,2})\s*月\s*(\d{1,2})\s*日\s*/, "$1-$2-$3");
处理页面上的所有 <input type="date" />
const inputs = document.querySelectorAll('input[type="date"]');
inputs.forEach((input) => {
input.addEventListener(...);
});
封装为独立域
避免全局变量污染,使用 IIFE 函数表达式:
(function() {
// 将代码放在这里
})();
或者封装为函数,在 jQuery 的 ready 中,或 Vue 的 mounted 中调用。
在 Vue 中使用
如果将粘贴板的值直接赋值到 input.value,在 Vue 中是不能同步更新 v-model 绑定的变量的,所以需要直接赋值给变量:
<div id="app">
<input type="date" v-model="a" data-model="a" v-on:paste="fn_pasteToDateInput" />
{{a}}
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const app = Vue.createApp({
data: function () {
return {
a: null,
}
},
methods: {
fn_pasteToDateInput: function (event) {
const text = event.clipboardData.getData('text');
const parsedDate = new Date(text);
if (!isNaN(parsedDate.getTime())) {
const att = event.target.getAttribute('data-model');
this[att] = parsedDate.toLocaleDateString('en-GB', { year: 'numeric', month: '2-digit', day: '2-digit' }).split('/').reverse().join('-');
}
},
}
});
const vm = app.mount('#app');
</script>
示例中 <input /> 添加了 data- 属性,值同 v-model,并使用 getAttribute() 获取,利用 this 对象的属性名赋值。
如果你的 a 中还有嵌套对象 b,那么 data- 属性填写 a.b,方法中以“.”分割逐级查找对象并赋值
let atts = att.split('.');
let target = this;
for (let i = 0; i < atts.length - 1; i++) {
target = target[atts[i]];
}
this.$set(target, atts[atts.length - 1], text);