html - 如何使用 HTML5 或/或 Angular8 实现持续时间选择器,小时数超过 24 小时?

标签 html angular angular-material angular8

我正在尝试使用其中之一来实现控件

 <input type="time"/>

或者只是用

  <input type="text"/>

并实现一个持续时间选择器控件,它的小时格式可以超过 24,例如 000:00:00 或 hhh:mm:ss,并且没有 am/pm 选项(时间的默认输入类型的格式为 am/pm 格式,这对我来说没有用)。 要求是能够使用向上和向下键增加减少持续时间,就像 HTML 的默认输入类型时间一样。

有没有原生的 HTMLangular 或 Material 组件? 或者有没有办法使用正则表达式/模式或其他方法来实现这一点?

最佳答案

我能想到的一种方法是编写您的自定义控件(@Allabakash 也提到过)。对于 native HTML,控件可以是这样的:

window.addEventListener('DOMContentLoaded', (event) => {
            document.querySelectorAll('[my-duration-picker]').forEach(picker => {
                //prevent unsupported keys
                const acceptedKeys = ['Backspace', 'ArrowLeft', 'ArrowRight', 'ArrowDown', 'ArrowUp'];
                const selectFocus = event => {
                    //get cursor position and select nearest block;
                    const cursorPosition = event.target.selectionStart;
                    "000:00:00" //this is the format used to determine cursor location
                    const hourMarker = event.target.value.indexOf(":");
                    const minuteMarker = event.target.value.lastIndexOf(":");
                    if (hourMarker < 0 || minuteMarker < 0) {
                        //something wrong with the format. just return;
                        return;
                    }
                    if (cursorPosition < hourMarker) {
                        event.target.selectionStart = 0; //hours mode
                        event.target.selectionEnd = hourMarker;
                    }
                    if (cursorPosition > hourMarker && cursorPosition < minuteMarker) {
                        event.target.selectionStart = hourMarker + 1; //minutes mode
                        event.target.selectionEnd = minuteMarker;
                    }
                    if (cursorPosition > minuteMarker) {
                        event.target.selectionStart = minuteMarker + 1; //seconds mode
                        event.target.selectionEnd = minuteMarker + 3;
                    }
                }
                const insertFormatted = (inputBox, secondsValue) => {
                    let hours = Math.floor(secondsValue / 3600);
                    secondsValue %= 3600;
                    let minutes = Math.floor(secondsValue / 60);
                    let seconds = secondsValue % 60;
                    minutes = String(minutes).padStart(2, "0");
                    hours = String(hours).padStart(3, "0");
                    seconds = String(seconds).padStart(2, "0");
                    inputBox.value = hours + ":" + minutes + ":" + seconds;
                }
                const increaseValue = inputBox => {
                    const rawValue = inputBox.value;
                    sectioned = rawValue.split(':');
                    let secondsValue = 0
                    if (sectioned.length === 3) {
                        secondsValue = Number(sectioned[2]) + Number(sectioned[1] * 60) + Number(sectioned[0] * 60 * 60);
                    }
                    secondsValue += 1;
                    insertFormatted(inputBox, secondsValue);
                }
                const decreaseValue = inputBox => {
                    const rawValue = inputBox.value;
                    sectioned = rawValue.split(':');
                    let secondsValue = 0
                    if (sectioned.length === 3) {
                        secondsValue = Number(sectioned[2]) + Number(sectioned[1] * 60) + Number(sectioned[0] * 60 * 60);
                    }
                    secondsValue -= 1;
                    if (secondsValue < 0) {
                        secondsValue = 0;
                    }
                    insertFormatted(inputBox, secondsValue);
                }
                const validateInput = event => {
                    sectioned = event.target.value.split(':');
                    if (sectioned.length !== 3) {
                        event.target.value = "000:00:00"; //fallback to default
                        return;
                    }
                    if (isNaN(sectioned[0])) {
                        sectioned[0] = "000";
                    }
                    if (isNaN(sectioned[1]) || sectioned[1] < 0) {
                        sectioned[1] = "00";
                    }
                    if (sectioned[1] > 59 || sectioned[1].length > 2) {
                        sectioned[1] = "59";
                    }
                    if (isNaN(sectioned[2]) || sectioned[2] < 0) {
                        sectioned[2] = "00";
                    }
                    if (sectioned[2] > 59 || sectioned[2].length > 2) {
                        sectioned[2] = "59";
                    }
                    event.target.value = sectioned.join(":");
                }
                const controlsDiv = document.createElement("div");
                const scrollUpBtn = document.createElement("button");
                const scrollDownBtn = document.createElement("button");
                scrollDownBtn.textContent = " - ";
                scrollUpBtn.textContent = " + ";
                scrollUpBtn.addEventListener('click', (e) => {
                    increaseValue(picker);
                });
                scrollDownBtn.addEventListener('click', (e) => {
                    decreaseValue(picker);
                });
                picker.parentNode.insertBefore(scrollDownBtn, picker.nextSibling);
                picker.parentNode.insertBefore(scrollUpBtn, picker.nextSibling);
                picker.value = "000:00:00";
                picker.style.textAlign = "right"; //align the values to the right (optional)
                picker.addEventListener('keydown', event => {
                    //use arrow keys to increase value;
                    if (event.key == 'ArrowDown' || event.key == 'ArrowUp') {
                        if(event.key == 'ArrowDown'){
                        decreaseValue(event.target);
                        }
                        if(event.key == 'ArrowUp'){
                        increaseValue(event.target);
                        }
                        event.preventDefault(); //prevent default
                    }

                    if (isNaN(event.key) && !acceptedKeys.includes(event.key)) {
                        event.preventDefault(); //prevent default
                        return false;
                    }
                });
                picker.addEventListener('focus', selectFocus); //selects a block of hours, minutes etc
                picker.addEventListener('click', selectFocus); //selects a block of hours, minutes etc
                picker.addEventListener('change', validateInput);
                picker.addEventListener('blur', validateInput);
                picker.addEventListener('keyup', validateInput);
            });
        });
<input type="text" my-duration-picker></input>

在 Google Chrome 78 上测试并工作。我稍后会做一个 Angular 版本。

对于 Angular 版本,您可以编写自己的自定义指令并将其导入到您的 app-module-ts 声明中。在 stackblitz 上查看此示例:

应用演示:https://angular-xbkeoc.stackblitz.io

代码:https://stackblitz.com/edit/angular-xbkeoc

更新:随着时间的推移,我开发并改进了这个概念。您可以在此处查看选择器 👉 https://nadchif.github.io/html-duration-picker.js/

关于html - 如何使用 HTML5 或/或 Angular8 实现持续时间选择器,小时数超过 24 小时?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58875798/

相关文章:

jquery - 更新网页中的变量而不用简单的方式重新加载它

javascript - 如何以像素为单位降低 VH 的值?

javascript - jQuery 使用 .text() 方法时如何保持换行符?

angular - 如何在 Angular 模板中迭代字典?

javascript - 通过字符串数组过滤对象数组

html - 行无排水沟不删除间距

javascript - 如何*ngFor 超过一组 ngModel 可绑定(bind)属性

javascript - Angularjs 验证不适用于复制粘贴

angular - Material 自动完成 : Link multiple text inputs to one autocomplete panel

javascript - 错误 : $compile:ctreq Missing Required Controller