How to make specific time range selectable in html - javascript

I want the time input field on specific times, so that only times from 8 AM to 8 PM can be selected. Bookings booked for 8:01 PM onwards, till booked for 7:59 AM can't be selected.
How can I make this custom time picker using JavaScript/Jquery?
<input name="booking-time" type="time" id="book_time">

To pick times within a required range, you can add a min and max:
function show() {
let t = document.getElementById("book_time");
console.log(t.value);
}
function show2() {
let t2 = document.getElementById("book_time2");
let t2Hour = t2.value.substr(0, 2);
let t2Minute = t2.value.substr(3, 2);
if (t2.value.substr(0, 2) < t2.min) {
alert("Earliest time is " + t2.min);
t2.value = t2.min;
} else if (t2.value.substr(0, 2) > t2.max){
alert("Latest time is " + t2.max);
t2.value = t2.max;
} else {
console.log(t2.value);
}
}
#book_time::after {
content: "___";
position: relative;
left: -20px;
line-height:1.5em;
background-color: white;
color: white;
}
<input type="time" id="book_time" name="booking-time" min="08:00" max="20:00" required onchange="show();">
<input type="time" id="book_time2" name="booking-time2" min="08:00" max="20:00" required onchange="show2();">
UPDATE
There does seem to be a browser issue. Some browsers will honour the min/max values, others don't. The ones that don't show a clock symbol to the right of the HH:MM display. The ones that do, show up/down arrows instead.
The updated snippet above offers two possible solutions.
The first one hides the clock symbol by covering it with a block of white text on a white background defined by the ::after pseudo-selector in a css style. The down-side is that there is nothing to tell the user what to do - they have to know to click in the hour part of the input and use their up/down arrow keys to change the hour value. Additionally, there will be whitespace at the right of the box that can not be removed.
The second one uses javascript to confirm that the hour selected falls within the allowable range. If they select anything outside of those hours, they get an alert and their time is set to either the min or max value as appropriate.
The only other solution would be to provide separate select lists for hours and minutes and restrict the range that way. That's not as elegent as a simple input box but does ensure that the user can not select an invalid value.

Although I am a fan of native (browser only) controls, date (and time pickers) are difficult to handle cross-browser wise. Therefore it is best to use an existing library and to find out what your requirements are exactly.
There are lengthy articles on the topic, it would not make sense to copy them here, therefore I'd like to redirect you there.
UX/Usablity of date/time pickers:
https://uxdesign.cc/ui-mechanics-of-a-date-picker-792f2aceb8aa
https://www.smashingmagazine.com/2017/07/designing-perfect-date-time-picker/
I personally like to use flatpickr.

Related

Is it possible to limit max-date on keyup?

Summary: I have 5 text inputs, one of which is using the Date attribute. I'm adding validation to the inputs, and one feature I would like to add is to limit the max date. I know I can do this in HTML, however it looks like that only works for the calendar functionality. My thoughts are using keyup, so the user is unable to exceed the year 2050 (for example) within the input in real time.
Effort: I looked into the Max attribute in HTML for a while, but it looks like I will have to do this manually in either Js or jQuery. My immediate thought is to pull the value from the input, slice the last 4 integers, and then create text validation from that.
I would rather use a text input and create the entire validation myself, but I would like to keep the calendar functionality without using any external resources. I'm curious if there is a simpler or more efficient way of doing this?
<input type="date" class="search__input" id="adv-input-2" placeholder="MM / DD / YY" max="2050-12-31"></span>
I've also noticed that using keyup is actually not firing when I use it on a date input, until the entire date is inputted:
// sets limits for date input
$('#adv-input-2').keyup(function(e) {
let input = $(this);
});
For example, 10/DD/YYYY will result in value: "" until the entire field is completed.
If you want to do this manually then you can use change event instead of keyup like this way.
// sets limits for date input
$('#adv-input-2').change(function(e) {
let input = $(this);
console.log(input.val());
if(input.prop('max') < input.val()) {
input.val('');
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<input type="date" class="search__input" id="adv-input-2" placeholder="MM / DD / YY" max="2050-12-31"></span>

How to get value from selected input field (not using id)

I try to do simple code for guessing notes by ear. I have tabs with several empty input fields and you need to put right numbers in these fields according to certain melody (for guitar fretboard) . One button shows first note, another button checks whether you put right or wrong number and depend on it approves or erase your number.
I know how to check every input field using its id's but can i do it such way that when i push 2nd button it get value from selected input and compare it to its placeholder or value attribute?
It is my codepen
https://codepen.io/fukenist/pen/BxJRwW
Script part
function showfirst() {
document.getElementById("fst").value = "12"
}
function show1other() {
var snote = document.getElementById("scnd").value;
if (snote == 9 ){
document.getElementById("scnd").value = "9";
}
else {
document.getElementById("scnd").value = "";
}
}
You can use document.querySelectorAll() to get all your inputs and loop over them.
Sample:
// Get all inputs as an array (actually NodeList, to be precise; but it behaves similar to an array for this use case)
var inputs = document.querySelectorAll('input');
// Function to reveal the first input's value
function showFirst(){
inputs[0].value = inputs[0].dataset.v;
}
// Function to check all values and clear input if wrong
function checkAll(){
inputs.forEach(function(input){
if(input.dataset.v !== input.value){
// Wrong answer, clear input
input.value = '';
}
});
}
<input data-v="12" size="2" value=""/>
<input data-v="9" size="2" value=""/>
<input data-v="8" size="2" value=""/>
<br/>
<button onclick="showFirst()">Show First</button>
<button onclick="checkAll()">Check All</button>
Notes:
I have used data-v to store the correct answer instead of placeholder as that attribute has a semantically different meaning
It may be out of turn but my two cents: Writing out entire songs like this by hand may become tedious. Consider using a JSON string or something similar to map out the tabs and use a templating framework to align them.. Some things you may need to look out for while designing something like this : Alignment of notes (successive notes, simultaneous notes), timing of the song, special moves like slide, hammer on etc.
It may be a better idea to make the Guitar Strings be a background element (either as a background-image or as absolutely positioned overlapping divs) (so You don't have to worry about the lines going out of alignment)
Reference:
HTMLElement.dataset
document.querySelectorAll

Angular event detection for datepicker close

I have just started using angular and in general love it. I have a UI requirement to show a date as text, then when you click on the text, it shows a date picker instead. Then when you select a date it goes back to text. Sounds simple.
I have tried a few variations on the below code the object being edited holds a value called editdate, clicking on the text box changes this to true and hides the text and shows the editor. This works fine, the problem is hiding the date picker. I tried adding ng-blur to the datepicker to cause the box to hide when it looses focus, but this was very intermittent as to if it worked. I then tried ng-Mouseleave but the box closed when I moved the mouse pointer into the date picker from the text box.
<td colspan="7">
Required Date : <span ng-click="item.editdate=true; item.delayeditclose = true;" ng-show="!item.editdate">{{item.dueDate.toDateString()}}</span>
<input type="date" ng-blur="item.editdate=false" ng-click="item.switcheditdate(item.delayeditclose)" ng-show="item.editdate" class="datepick" ng-model="item.dueDate" ng-focus="selectPart(item.partNumber)" />
</td>
The javascript is initialized as:
for (var i = 0; i < data.items.length; i++) {
item = data.items[i];
item.showExpanded = false;
item.editdate = false;
item.delayeditclose = true;
item.switcheditdate = function (delayclose) {
if (!delayclose) {
this.editdate = !this.editdate;
this.delayeditclose = true;
}
this.delayeditclose = false;
};
items.push(item);
}
I have now moved onto the above which almost does the job. The new issue being html5 date pickers seem to have a x to close them down. This hides the whole box and not just the picker part. If this happens I want to show the text of the date rather than what I get now which is nothing. Does anyone know of how I can capture this close event and revert back to the text or suggest a better approach to the problem?
I have lots of items each one is in its own row in a table
I am using angular 1.3 beta 8
thanks for looking
Andy
The reason why you are getting nothing is because when you click x what happens is that the date value gets cleared so then your date view is an empty string. What you can do is set a condition to display a string such as "Click to Edit" when the date is empty.
Taking just what you have above, one way of doing it is by adding an element and a flag in your date "view". So change this:
Required Date : <span ng-click="item.editdate=true; item.delayeditclose = true;" ng-show="!item.editdate">{{item.dueDate.toDateString()}}</span>
To:
Required Date :
<span ng-click="item.editdate=true; item.delayeditclose = true;" ng-show="!item.editdate">
<span>{{item.dueDate.toDateString()}}</span>
<span ng-show="!item.dueDate">Click to Edit</span>
</span>
The directive way
Now in my opinion I think it's much better to use a directive for this. It would simplify your logic and is much easier to re-use. I created a plunker which shows my implementation using a directive.
Answer to question in title
To answer the question posed by your title, when using the html5 date picker, you can't detect when it is closed (or even when it is opened). See this answer and the linked Quick FAQs on input[type=date]

How to use HTML to validate a date range?

Alternatively, is it possible to validate against another field's value with HTML?
A common example would be selecting a date range where "from" date should be less than or equal to "to" date. The following would described the desired relationship between the values, if only you could use element references in syntax:
<input type="date" name="from" max="to"> //todo: populate with ~to.value
<input type="date" name="to" min="from"> //todo: populate with ~from.value
It's possible to utilize html5 validation mechanism with some javascript to dynamically update min/max attributes:
//in this case a single input restriction is sufficient to validate the form:
$('#from, #to').on('change', function(){
$('#to').attr('min', $('#from').val());
});
Fiddled. Both min and max could be applied to the respective fields for enhanced UX if browser implementation of a datepicker respects range limitations (by disabling dates outside of the desired range)
Here, Web Components are very useful, however they are not full supported in all browsers yet .
The idea is to create a simple html Element, with two children (from and to) as the following:
<div id="fromToDate">
<div></div>
<div></div>
</div>
then create a template, which defines how the date picker should look:
<template id="fromToDateTemplate">
<label for="fromDate">from</label>
<input type="date" class="fromDate" select=":first" required="" />
<label for="toDate">to</label>
<input type="date" class="toDate" select=":last" required="" />
</template>
the select parameter defines, where the value is taken from so the first input field takes the first div from the "#fromToDate".
Last we have to populate the "shadow root" and define the logic:
var shadow = document.querySelector('#fromToDate').webkitCreateShadowRoot(),
template = document.querySelector('#fromToDateTemplate');
shadow.appendChild(template.content);
shadow.querySelector(".fromDate").addEventListener("change", function (e) {
var to = this.value;
shadow.querySelector(".toDate").setAttribute("min", this.value);
});
template.remove();
In the end two input fields are renderd and when selecting a date in the first datepicker, the second datepicker can't pick any lower data.
Fiddler example: http://jsfiddle.net/cMS9A/
Advantages:
Build as widget
Easy to reause
won't break pages
can be styled independently
Disadvantages:
Not supported in all browsers yet
Future reading:
http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom/
http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom-201/
https://dvcs.w3.org/hg/webcomponents/raw-file/tip/explainer/index.html
If you want to avoid issues with someone hacking / crashing yor site - validate input data with:
(optional) javascript before sending a form (protects against malforming data using javascript, inputting incorrect one, reduces traffic)
(mandatory) on server side (protects against more clever guys that might malform input data using fiddler for example)
This is the only (at least second point) approach that protects you and your site.
It's great to see things moving towards a pure HTML solution ... but why not take a look at using moment.js to fill in the gaps for the time being?
http://momentjs.com/
There are plenty of good examples there and a lot of useful utility methods.
I'm worry, that there's no chance how to validate a input value based on other input value. Only good old javascript.
But maybe you can use <input type="range" …> and set some minimal step (1 day / 1 hour / …). Then you can use min and max value by same-named attributes.

Is there a prewritten low-impact time drop down that allows selection of any of the 1440 minutes that there are in a day?

I will probably be writing my own.. I wanted to check if anybody already knew of one.
There will be multiple time drop downs in a single form. They will not be <select..> tags with 1440 options per select... They will have a default scrolled position, and they will look like a single drop down with 1440 items but will only display the scrolled position's items and the individual items will only be stored for the focused dropdown.
1440 items in any drop-down list is ill-advised. Especially if there are more than one of them on a page.
Why not look for some other time entry control?
http://keith-wood.name/timeEntry.html
http://pttimeselect.sourceforge.net/example/index.html
http://haineault.com/media/jquery/ui-timepickr/page/
... or break it down into 2 selcts - 1 for the hour and 1 for the minute
A drop-down with 1440 entries just isn't going to be usable.
How about a normal text box so that users can just type the time they want, quickly (and the input will still work without JavaScript), coupled with up/down spin controls if you need that:
<input type="text" class="spin-time" value="00:00">
<script type="text/javascript">
// Find and apply to elements with class `spin-time`
//
var inputs= document.getElementsByTagName('input');
for (var i= inputs.length; i-->0;)
if (inputs[i].className==='spin-time')
addTimeSpinner(inputs[i]);
function addTimeSpinner(input) {
input.onchange= function() { alterTime(0); };
input.parentNode.insertBefore(makeButton('-', -60), input);
input.parentNode.insertBefore(makeButton('+', 60), input);
input.parentNode.insertBefore(makeButton('+', 1), input.nextSibling);
input.parentNode.insertBefore(makeButton('-', -1), input.nextSibling);
function makeButton(label, d) {
var button= document.createElement('input');
button.type= 'button';
button.value= label;
button.onclick= function() { alterTime(d); };
return button;
}
function alterTime(d) {
// Parse time value, default to 00:00 if doesn't make sense
//
var mins= 0;
var match= input.value.match(/^(\d\d):(\d\d)$/);
if (match!==null)
mins= parseInt(match[1], 10)*60+parseInt(match[2], 10);
// Add time difference, wrapping round
//
mins= (mins+d+1440)%1440;
// Format back to hh:mm string
//
input.value= padLeft(Math.floor(mins/60), 2, '0')+':'+padLeft(mins%60, 2, '0');
}
function padLeft(v, n, c) {
v+= '';
if (v.length>=n) return v;
return new Array(n-v.length+1).join(c)+v;
}
}
</script>
This is 24-hour. Altering the code to parse and produce the 12-hour clock format (including the horrid wart of getting 12 AM and PM the right way around) is left as an exercise for someone who doesn't completely hate the idea of the 12-hour clock and want to see it eradicated from the Earth.
I would use HTML5 and:
<input type="time" />
And perhaps add a jQuery time picker thing if needed. But for the most part I seriously find it quicker to just type in the time rather than find it in a list of some sort. Just make sure you validate and have some easy to read error messages, et cetera...
I ended up with an input that opens a list of 20 links showing different times. The top and bottoms had previous/next links that looked like page up and page down functions. The middle of the list was either the currently selected option or the current time.

Categories