Reputation: 33
As you see in the code, i want to let users select the time between opening and closing time (min.max), but its not working, it'll take all time entered and some times it'll work only for max time. I've seen similar questions, i couldn't find any solutions.
part of code -
temple_name| temple_opentime | temple_closetime
-----------------------------------------------
temple1 | 9:30 | 18:30 |
temple2 | 7:30 | 21:00 |
temple3 | 10:00 | 18:00 |
temple4 | 8:30 | 20:00 |
<select id="temple" class="form-control">
<?php
require "config/database.php";
$sql = "select * from entity_temple";
$result = mysqli_query($link, $sql);
while($row = mysqli_fetch_array($result))
{
echo '<option name="'.$row["temple_opentime"].'" data-value="'. $row["temple_closetime"].'" value="'. $row["temple_name"].'">'. $row["temple_name"].'</option>';
}
?>
</select>
<div class="form-group col-md-4">
<label for="time" class="form-label">Time:</label>
<input type="time" id="time" name="time" min="" max="" step="600" class="form-control">
</div>
<div class="form-group d-grid col-4 mx-auto">
<button type="submit" name="save" class="btn btn-block btn-outline-dark" id="butsave">Add Request</button>
</div>
$("#temple").on("change", function () {
var value = $('#temple').val();
var nm = $('#temple option[value="' + value + '"]').attr('name');
var dv = $('#temple [value="' + value + '"]').data('value');
$("#time").attr({
"max" : dv,
"min" : nm
});
Upvotes: 0
Views: 3034
Reputation: 7086
As stated above, the problem with the OP code was merely a typo:
The input's min
and max
attribute values must be strings in the form "hh:mm" ("h:mm" is invalid):
<input type="time" min="08:30" max="16:00">
However, this typo fix alone does not address browser incompatibilities. As MDN states:
... in Safari, and any other browsers that don't support
<time>
, it [<input type="time">
] degrades gracefully to<input type="text">
.
and a degraded input
will not be checked for min/max errors.
To address this browser incompatibility, the code below detects inputs that have been degraded to the form <input type="text" min="hh:mm" max="hh:mm">
, and adds a validator script to the form.
If a validation error is detected, the error message will be shown in an overlay above the input (matching the native behavior in Chrome and Firefox).
// the "real" form submits
const timeForm = document.getElementById('formtime');
timeForm.onsubmit = function () {
alert('time form was submitted');
};
const textForm = document.getElementById('formtext');
textForm.onsubmit = function () {
alert('text form was submitted');
};
// time validator for degraded time inputs
// Note: This must be loaded AFTER all user form submitters have
// been added
(function () {
// error overlay custom element
class ErrorOverlay extends HTMLElement {
constructor () {
super();
// create a shadow root
let shadowRoot = this.attachShadow({mode: 'open'});
// create shadow DOM element
const overlay = document.createElement('div');
this.overlay = overlay;
overlay.className = 'error-overlay';
// create css for the shadow DOM
const style = document.createElement('style');
style.textContent = `
.error-overlay {
font-family: Helvetica, sans-serif;
position: absolute;
z-index: 1000;
border: 1px solid #808080;
border-radius: 4px;
color: #f8f8f8;
background-color: #808080;
padding: 0.2rem 0.4rem;
display: none;
}
.error-overlay::after {
content: " ";
position: absolute;
top: 100%;
left: 24px;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: #808080 transparent transparent transparent;
}
`;
// attach shadow DOM to shadow root
shadowRoot.appendChild(style);
shadowRoot.appendChild(overlay);
}
// show error overlay above the input
show (input, message) {
const {top, right, bottom, left} = input.getBoundingClientRect();
const overlay = this.overlay;
overlay.style.top = Math.round(top - 34 + window.scrollY) + 'px';
overlay.style.left = Math.round(left) + 'px';
overlay.innerText = message;
overlay.style.display = 'block';
// hide overlay
function overlayHide () {
overlay.style.display = 'none';
// remove listeners
input.removeEventListener('keyup', overlayHide);
input.removeEventListener('blur', overlayHide);
}
// add input listeners and focus the input
input.addEventListener('keyup', overlayHide);
input.addEventListener('blur', overlayHide);
input.focus();
}
}
// create overlay instance & add to body
customElements.define('error-overlay', ErrorOverlay);
const errorOverlay = document.createElement('error-overlay');
document.body.appendChild(errorOverlay);
// convert time string to decimal hours
// time string patterns: "hh:mm AM", "hh:mm PM", "hh:mm"
function timeStringToDecimalHours (timeStr) {
let [hhmm, ampm] = timeStr.split(' ');
ampm = (ampm || '').toLowerCase();
let [hour, minute] = hhmm.split(':').map(el => Number(el));
if (ampm === 'pm' && hour < 12) {
hour += 12;
} else if (ampm === 'am' && hour === 12) {
hour = 0;
}
return hour + (minute / 60);
}
// get array of inputs that have this pattern:
// <input type="text" min="hh:mm" max="hh:mm">
function getDegradedTimeInputs (form) {
return Array.from(form.querySelectorAll('input'))
.filter(input => {
if (input.type !== 'text') { return false; }
return input.hasAttribute('min') ||
input.hasAttribute('max');
});
}
// validate inputs when form is submitted
function validateTimeOnFormSubmit (evt) {
// get form's inputs that have type="text", `min` and/or `max`
const form = evt.target;
const tInputs = getDegradedTimeInputs(form);
// validate that each input value is between min and max
for (const input of tInputs) {
const {value, max, min} = input;
// convert time strings into decimal hours
const valTime = timeStringToDecimalHours(value),
minTime = timeStringToDecimalHours(min),
maxTime = timeStringToDecimalHours(max);
if (valTime < minTime || maxTime < valTime) {
// show the error overlay and prevent the submit
errorOverlay.show(input, `Time must be between ${min} and ${max}`);
return false;
}
}
// complete the submit
return form.realOnSubmit(evt);
}
// add time validator to all forms that contain degraded time inputs
const forms = document.querySelectorAll('form');
function submitHandlerStub () { return true; }
for (const form of forms) {
if (getDegradedTimeInputs(form).length > 0) {
// store the real submit handler as `form.realOnSubmit`
const realOnSubmit = form.onsubmit || submitHandlerStub;
Object.defineProperty(form, 'realOnSubmit', {
value: realOnSubmit,
enumerable: false
});
form.onsubmit = validateTimeOnFormSubmit;
}
}
})();
form {
border: 1px solid black;
max-width: 30rem;
margin: 1rem 0;
padding: 0.5rem;
}
<h1>Validate a form's degraded time inputs</h1>
<p>Some browsers do not support <code><input type="time"></code>, degrading it to <code><input type="text"></code>.</p>
<p>This script detects these degraded inputs and validates them accordingly.</p>
<form id="formtime">
<label for="timeinput">TIME input (min 09:30, max: 14:00):</label>
<input id="timeinput" type="time" min="09:30" max="14:00" placeholder="hh:mm AM/PM" />
<button type="submit">Submit</button>
</form>
<form id="formtext">
<label for="degraded">TEXT input (min 09:30, max: 14:00):</label>
<input id="degraded" type="text" min="09:30" max="14:00" style="width: 5rem" placeholder="hh:mm AM/PM" />
<button type="submit">Submit</button>
</form>
Upvotes: 1
Reputation: 30883
Consider the following.
$(function() {
$("#temple").on("change", function() {
var item = $(":selected", this),
op = item.data("open-time"),
cl = item.data("close-time");
$("#time").attr({
min: op,
max: cl,
value: op
}).trigger("input");
});
$("#time").on("change", function(e) {
if ($(this).val() < $(this).attr("min")) {
$(this).val($(this).attr("min"));
} else if ($(this).val() > $(this).attr("max")) {
$(this).val($(this).attr("min"));
}
});
});
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!--
temple_name| temple_opentime | temple_closetime
-----------------------------------------------
temple1 | 9:30 | 18:30 |
temple2 | 7:30 | 21:00 |
temple3 | 10:00 | 18:00 |
temple4 | 8:30 | 20:00 |
-->
<select id="temple" class="form-control">
<option>---</option>
<option data-open-time="09:30" data-close-time="18:30" value="Temple1">Temple1</option>
<option data-open-time="07:30" data-close-time="21:00" value="Temple2">Temple2</option>
<option data-open-time="10:00" ddata-close-time="18:00" value="Temple3">Temple3</option>
<option data-open-time="08:30" data-close-time="20:00" value="Temple1">Temple4</option>
</select>
<div class="form-group col-md-4">
<label for="time" class="form-label">Time:</label>
<input type="time" id="time" name="time" min="" max="" step="600" class="form-control" required>
</div>
<div class="form-group d-grid col-4 mx-auto">
<button type="submit" name="save" class="btn btn-block btn-outline-dark" id="butsave">Add Request</button>
</div>
Here, when a Option is selected, the Min, Max, and Value of the Time input are update. once updated, we trigger an Input event, this will cause the Value to appear to the User. Values can be in the Input that are outside the Min and Max, this will cause the Input to be invalid. If the User then adjusts the time, it will forced to match the Min Max via jQuery.
See More: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/time#Time_value_format
Note: When the data entered by the user doesn't adhere to the stepping configuration, the user agent may round to the nearest valid value, preferring numbers in the positive direction when there are two equally close options.
This property has some strange effects across browsers, so is not completely reliable.
Upvotes: 1
Reputation: 33933
I would use the data attributes for the open and close time... And with meaningful names. ;)
Your SQL should be:
while($row = mysqli_fetch_array($result)){
echo '<option value="'.$row["temple_name"].'" data-opentime="'. $row["temple_opentime"].'" data-closetime="'. $row["temple_closetime"].'">'. $row["temple_name"].'</option>';
}
I noticed the time is NOT showing in the input if the value does not have a leading zero... So 7:30
doesn't work but 07:30
works.
Now about the min
and max
attributes of the time input...
There is no validation made out of the box by the browser yet...
You can notice that even the MDN example is not behaving has it should.
So I added somekind of a basic validation starter. It at least prevents the form submit and adds a class to the time element if the chosen value is incorrect.
$("#temple").on("change", function() {
var value = $('#temple').val();
var min = $('#temple option[value="' + value + '"]').data('opentime');
var max = $('#temple [value="' + value + '"]').data('closetime');
console.log(value, min, max)
$("#time").attr({
value: min,
max,
min
});
});
$("#timeForm").on("submit", function(e){
e.preventDefault();
let time = $("#time");
if( time.val() < time.attr("min") || time.val() > time.attr("max") ){
time.addClass("error");
return;
}
// if the time is correct, submit
$(this).submit();
});
$("#timeForm").on("change",function(){
$("#time").removeClass("error");
});
.error{
background: red;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"></script>
<form id="timeForm">
<select id="temple" class="form-control">
<option disabled selected>Select a temple</option>
<option value="temple1" data-opentime="09:30" data-closetime="18H30">temple1</option>
<option value="temple2" data-opentime="07:30" data-closetime="21:00">temple2</option>
<option value="temple3" data-opentime="10:00" data-closetime="18:00">temple3</option>
<option value="temple4" data-opentime="08:30" data-closetime="20:00">temple4</option>
</select>
<div class="form-group col-md-4">
<label for="time" class="form-label">Time:</label>
<input type="time" id="time" name="time" min="" max="" step="600" class="form-control" required>
</div>
<div class="form-group d-grid col-4 mx-auto">
<button type="submit" name="save" class="btn btn-block btn-outline-dark" id="butsave">Add Request</button>
</div>
</form>
Upvotes: 1