Form tag-like inputs - html

I have no idea what this is even called so not sure where to start, hoping you all are able to point me in the right direction. I am trying to create something like this:
The idea being that when a user types something in and then separates with a comma, their input into this form field will turn into a little tag-like box that they can then delete with the x.
Any ideas on where to start?
Thanks!

Here is simple example: http://jsfiddle.net/9tzb4/
HTML:
<form>
Tags:
<div class="tag_field">
<input type="text">
<input type="hidden" name="tags">
</div>
<br>
<input type="submit" value="Submit">
</form>
CSS:
.tag_buttons {
position: absolute;
}
.tag_buttons div {
display:inline-block;
margin: 2px;
border: 1px solid #666;
padding: 1px;
}
.tag_field input[type=text] {
padding: 5px;
width: 300px;
border: 1px solid #666;
}
JavaScript:
$(".tag_field").each(function() {
var buttons = $("<div/>");
var input = $(this).find("input[type=text]");
var output = $(this).find("input[type=hidden]");
var update_padding = function() {
input.css("padding-left", buttons.width() + 2);
};
setInterval(update_padding, 300);
$(this).prepend(buttons);
buttons.addClass("tag_buttons");
buttons.css({ left: input.offset().left + 1,
top: input.offset().top + 2 });
input.bind("keyup change paste", function() {
var i = input.val().indexOf(",");
if (i >= 0) {
var new_tag = input.val().substr(0, i);
input.val(input.val().substr(i+1));
buttons.append("<div id='button'><span class='value'>"+new_tag+"</span> <span class='close'>(x)</span></div>");
}
});
var form = $(this).closest("form");
if (form.length > 0) {
form.submit(function() {
var v = [];
form.find(".tag_buttons div").each(function() {
v.push($(this).find(".value").html());
});
output.val(v.join(","));
return false;
});
}
});
$(document).on("click", ".tag_buttons span.close", {}, function(e) {
$(e.target).closest("div").detach();
});

Check this out. http://davidwalsh.name/dw-content/jquery-chosen.php This is the things I guess will perfectly suite your application.

Check out the amazing jQuery chosen, which - among other things - does exactly what you're looking for.
select2 is a similar plugin, and it also allows you to add your own tags (as well as use an AJAX source).
Update from the select2 docs:
Note that when tagging is enabled the user can select from pre-existing tags or create a new tag by picking the first choice which is what the user has typed into the search box so far.

You may find the jQuery Tags Input Library helpful. It gives you something like this

Related

Range Slider Input

I'm having a bit of hard time trying to figure out how to work this.
First of all, I have a range input slider. The slider has 5 tiers of increments, but the thing is, under the slider's ball there's a label that says "0/5", "1/5", "2/5" etc. Is there a way to display the label the following: at default start should be "0" then "0-10", "10-25", "25-100", "100-500", and lastly "500+".
<input id="range_size_value" type="text" class="rangeSliderRate"
data-min="0.0" data-max="5.0" data-from="0"
data-step="1"
name="range_size_value">
<input hidden readonly name="size" id="hidden_input_value" type="text" value="0">
$('#range_size_value').on('input', function (e) {
let size_value = $(".irs-single");
let input_step_value = $(this).val();
let hidden_input_value = $('#hidden_input_value');
if (input_step_value === '0') {
size_value.attr('value', '0');
hidden_input_value.attr('value', '0');
}
if (input_step_value === '1') {
size_value.html('0-10');
hidden_input_value.attr('value', '0-10');
}
if (input_step_value === '2') {
size_value.html('10-25');
hidden_input_value.attr('value', '10-25');
}
if (input_step_value === '3') {
size_value.html('25-100');
hidden_input_value.attr('value', '25-100');
}
if (input_step_value === '4') {
size_value.html('100-500');
hidden_input_value.attr('value', '100-500');
}
if (input_step_value === '5') {
size_value.html('500+');
hidden_input_value.attr('value', '500+');
}
});
That is my code that I have. It sort of works, but not really. For example, when I hold the mouse button down and slide the bar to the right, you can see the label changes to "0-10", "10-25" etc., but as soon as I let go of the mouse button, it goes back to "1/5" etc.
Also, there's a class called "irs-single" which is a span that controls the label for display the value. I don't see this span element nowhere in my html code. I only see in the Google dev tools when I inspect the elements.
If there's a better way of doing this, please definitely point me at the right direction. I feel like there is a better way than what I coded. I feel that there's a lot of conditional "if" statements. But I would like to keep the styling of the slider. I think this is more of a logic situation.
So I'm going to break this down in steps because this method can be a little bit much.
First, a div will be created to contain the range slider and the indicator.
Next, the range slider will be made.
An event listener will be added to check when the value of the range slider changes. When that happens, the position as well as the innerHTML of the indicator box will change.
Rather than using dataset like in the example you provided, I'll simply be using the value attribute of the range slider. These will be whole numbers and correspond to an array, with each part of the range slider corresponding with an element in the array. This way we can get the values that will appear in the box without having to hard-code them.
var values = ["0-10","10-25","25-100","100-500","500+"];
// event listener
document.getElementById("mySlider").addEventListener("input",function(e){
var text = values[e.target.value];
document.querySelector(".indicator").style.left = Number((e.target.value*25)-(2*e.target.value)) + "%";
// The subtraction portion ensures that the indicator doesn't go off the screen. Feel free to change the 2 to any value that works for you.
document.querySelector(".indicator").innerHTML = text;
});
* {
margin: 0;
box-sizing: border-box;
}
.container {
background-color: #ccc;
width: 100%;
display: block;
position: relative;
}
input[type=range] {
width: 100%;
display: block;
}
.indicator {
font-family: Arial;
padding: 5px;
background-color: black;
color: white;
position: absolute;
left: 0;
border-radius: 5px;
}
<div class="container">
<input type="range" min="0" value="0" max="4" step="1" id="mySlider">
<div class="indicator">
0-10
</div>
</div>

HTML multiple file upload from different folders

I have a form with an upload field that allows users to select multiple files. However, I need to be able to allow the user to select file 1 from folder 1, then go and select file 2 from folder 2, and so on.
Currently, when the user selects file 1 from folder 1 then hits "Open", the selection window closes (leaving the user on my form). Then if the user goes and select file 2 from folder 2 and hits the "Open" button, file 1 is removed, leaving only file 2.
Basically, the user is unable to select multiple files unless they're all in the same location. Is there a way to make file 1 stay selected after file 2 is chosen?
How about this?
The solution uses HTML, jQuery/Javascript, and PHP (for server-side handling of this data). The idea is: 1.) HTML Form: Includes one "browse" button that allows the user to select multiple files (within one directory). 2.) jQuery: The option to create a new button in the form that allows users to select multiple files (within a different directory -- or even the same one actually!), with the ability to create new buttons "infinitely". 3.) PHP: As a bonus, I put some thought into packaging the data nicely for server-side handling.
Here is what the HTML form could look like (I used a found-icon for the clickable object, but you can easily replace it with a graphic of your choosing).
<form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>" enctype='multipart/form-data'>
Select files: <br/>
<input type='file' name='files0[]' id="files0" multiple><br/><br/><br/>
<span style="font-size: 10pt;">Click "+" for more files
<i id="more_files" class="general foundicon-plus" style="color: blue;cursor: pointer;"></i></span>
<br/><br/><br/>
<input type="submit" name="submit" value="Submit">
</form>
Here is the jQuery/Javascript to create a new "browse" button once the event is triggered (this even places it after the LAST "browse" button!):
<script type="text/javascript">
//jQuery
$(document).ready(function() {
$(document).on('click','#more_files', function() {
var numOfInputs = 1;
while($('#files'+numOfInputs).length) { numOfInputs++; }//once this loop breaks, numOfInputs is greater than the # of browse buttons
$("<input type='file' multiple/>")
.attr("id", "files"+numOfInputs)
.attr("name", "files"+numOfInputs+"[]")
.insertAfter("#files"+(numOfInputs-1));
$("<br/>").insertBefore("#files"+numOfInputs);
});
});
</script>
<script>
//vanilla javascript version
var location = document.getElementById("fileBrowsers");
var br = document.createElement("BR");
location.appendChild(br);
var input = document.createElement("input");
input.type = "file";
input.name = "files"+numOfInputs+"[]";
input.id = "files"+numOfInputs;
input.multiple = true;
location.appendChild(input);
</script>
Finally, and possibly most important, how to wrap up the data on the server in a familiar format:
<?php
if(isset($_POST['submit']) && !empty($_FILES)) {
$files = array();
$files = $_FILES['files0'];
//var_dump($files);//this array will match the structure of $_FILES['browser']
//Iterate through each browser button
$browserIterator = 1;
while(isset($_FILES['files'.$browserIterator])) {
//Files have same attribute structure, so grab each attribute and append data for each attribute from each file
foreach($_FILES['files'.$browserIterator] as $attr => $values) {//get each attribute
foreach($_FILES['files'.$browserIterator][$attr] as $fileValue) {//get each value from attribute
$files[$attr][] = $fileValue;//append value
}
}
$browserIterator++;
}
//Use $files like you would use $_FILES['browser'] -- It is as though all files came from one browser button!
$fileIterator = 0;
while($fileIterator < count($files['name'])) {
echo $files['name'][$fileIterator]."<br/>";
$fileIterator++;
}
}
?>
Update Note: jQuery script and vanilla Javascript accomplish the same goal. I ran into an issue that required the vanilla version. You only need one of them.
No, you can't. This is a behaviour defined by the operating systems and may vary between them. You can't control these things precisly and you will always fear what happen.
If the amount of folders people have to choose is quite small you could offer multiple upload fields.
Another solution is using old school (non-multiple) file inputs. In this case you cannot select multiple files to upload, but you can remove any file and add another. Initially there is only one file input on page, but when you select a file, it hiding and replacing by filename with delete button, and new file input appears.
var fileInput = document.getElementById('fileInput_0');
var filesList = document.getElementById('fileList');
var idBase = "fileInput_";
var idCount = 0;
var inputFileOnChange = function() {
var existingLabel = this.parentNode.getElementsByTagName("LABEL")[0];
var isLastInput = existingLabel.childNodes.length<=1;
if(!this.files[0]) {
if(!isLastInput) {
this.parentNode.parentNode.removeChild(this.parentNode);
}
return;
}
var filename = this.files[0].name;
var deleteButton = document.createElement('span');
deleteButton.innerHTML = '×';
deleteButton.onclick = function(e) {
this.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode);
}
var filenameCont = document.createElement('span');
filenameCont.innerHTML = filename;
existingLabel.innerHTML = "";
existingLabel.appendChild(filenameCont);
existingLabel.appendChild(deleteButton);
if(isLastInput) {
var newFileInput=document.createElement('input');
newFileInput.type="file";
newFileInput.name="file[]";
newFileInput.id=idBase + (++idCount);
newFileInput.onchange=inputFileOnChange;
var newLabel=document.createElement('label');
newLabel.htmlFor = newFileInput.id;
newLabel.innerHTML = '+';
var newDiv=document.createElement('div');
newDiv.appendChild(newFileInput);
newDiv.appendChild(newLabel);
filesList.appendChild(newDiv);
}
}
fileInput.onchange=inputFileOnChange;
#fileList > div > label > span:last-child {
color: red;
display: inline-block;
margin-left: 7px;
cursor: pointer;
}
#fileList input[type=file] {
display: none;
}
#fileList > div:last-child > label {
display: inline-block;
width: 23px;
height: 23px;
font: 16px/22px Tahoma;
color: orange;
text-align: center;
border: 2px solid orange;
border-radius: 50%;
}
<form enctype="multipart/form-data" method="post">
<div id="fileList">
<div>
<input id="fileInput_0" type="file" name="file[]" />
<label for="fileInput_0">+</label>
</div>
</div>
</form>
document.querySelector("input").addEventListener("change", list_files);
function list_files() {
var files = this.files;
for (var i = 0; i < files.length; i++) {
console.log(files[i].name);
}
}
<input type="file" multiple>

How to process which button is selected in an HTML form?

I'm creating a nice-ish HTML form on my website for a school project (though this question is not homework)
The form takes product specifications from input and lists them for sale or looks if they are available. The action the website takes depends on whether the user is a buyer or seller.
At the top of the form there are two nice buttons - "Buyer" and "Seller". The user selects one and proceeds with his details. However, when the form is processed, I can't think of a simple way to check which button was selected. With radio buttons it was easy, but I want to use full buttons like these because they will look much nicer on the site.
I've made a sample on JSFiddle- http://jsfiddle.net/6u6jN/1/
For a user who clicks "Buyer" and types the name "Sarah", the result I want is "You are a Buyer named Sarah".
Essentially, my question can be summed up as: How do you use ordinary buttons as though they were radio buttons?
Code from JSFiddle, as required by StackOverflow:
HTML:
<form>
<button type="button" class="mybutton" id="buyer">Buyer</button>
<button type="button" class="mybutton" id="seller">Seller</button>
<input type="text" id="inputBox" name="name" />
</br>
<button type="button" id="submit" onClick="inputProcess(this.form)">Enter</button>
</form>
<div id="results"></div>
CSS:
.mybutton {
padding: 20px;
font-family: inherit;
font: inherit;
color: inherit;
text-shadow: inherit;
text-align: center;
border-radius: 6px;
position: relative;
background: #0ff551;
border: 2px solid #c2e1f5;
display: inline-block;
}
#inputBox {
height: 40px;
width: 200px;
font-size: 30pt;
}
JavaScript:
function inputProcess(form){
var name = form.name.value;
var resultsDisplay = document.getElementById("results");
resultsDisplay.innerHTML = "<p> You are a ????? whose name is " + name + "</p>";
}
You need to add event listeners on the buttons, and then when the button is clicked you save the result in a variable, something like this:
var result;
document.getElementById('seller').addEventListener('click', function() {
result = this.textContent;
}, false);
document.getElementById('buyer').addEventListener('click', function() {
result = this.textContent;
}, false);
document.getElementById('submit').addEventListener('click', function() {
var name = document.getElementById('inputBox').value;
var resultsDisplay = document.getElementById("results");
resultsDisplay.innerHTML = "<p> You are a " + result + " whose name is " + name + " </p>";
}, false);
Also I would avoid attaching event directly to HTML like <button onclick="?"></button>, it's much cleaner to just attach the listener onload with document.addEventListener
JSFiddle

Not showing placeholder for input type="date" field

I am doing a phonegap app. When I am trying type="date" input field as shown below, it shows date picker in iPhone as I expected but it doesn't show the placeholder I have given. I found the same issue here in SO, but no solution anywhere.
<input placeholder="Date" class="textbox-n" type="date" id="date">
It may not be appropriate... but it helped me.
What I did is start with a text input field, then change the type to a date input when the input is in focus.
<input
placeholder="Date"
class="textbox-n"
type="text"
onfocus="(this.type='date')"
id="date" />
If you use mvp's method but add the onblur event to change it back to a text field so the placeholder text appears again when the input field looses focus. It just makes the hack a little bit nicer.
<input placeholder="Date" class="textbox-n" type="text" onfocus="(this.type='date')" onblur="(this.type='text')" id="date" />
I ended up using the following.
Regarding Firefox comment(s): Generally, Firefox will not show any text placeholder for inputs type date.
But as this is a Cordova/PhoneGap question this should be of no concern (Unless you want to develop against FirefoxOS).
input[type="date"]:not(.has-value):before{
color: lightgray;
content: attr(placeholder);
}
<input type="date" placeholder="MY PLACEHOLDER" onchange="this.className=(this.value!=''?'has-value':'')">
As of today (2016), I have successfully used those 2 snippets (plus they work great with Bootstrap4).
Input data on the left, placeholder on the left
input[type=date] {
text-align: right;
}
input[type="date"]:before {
color: lightgrey;
content: attr(placeholder) !important;
margin-right: 0.5em;
}
Placeholder disappear when clicking
input[type="date"]:before {
color: lightgrey;
content: attr(placeholder) !important;
margin-right: 0.5em;
}
input[type="date"]:focus:before {
content: '' !important;
}
I used this in my css:
input[type="date"]:before{
color:lightgray;
content:attr(placeholder);
}
input[type="date"].full:before {
color:black;
content:""!important;
}
and put somenthing like this into javascript:
$("#myel").on("input",function(){
if($(this).val().length>0){
$(this).addClass("full");
}
else{
$(this).removeClass("full");
}
});
it works for me for mobile devices (Ios8 and android). But I used jquery inputmask for desktop with input text type. This solution it's a nice way if your code run on ie8.
Based on deadproxor and Alessio answers, I would try only using CSS:
input[type="date"]::before{
color: #999;
content: attr(placeholder) ": ";
}
input[type="date"]:focus::before {
content: "" !important;
}
And if you need to make the placeholder invisible after writing something in the input, we could try using the :valid and :invalid selectors, if your input is a required one.
EDIT
Here the code if you are using required in your input:
input[type="date"]::before {
color: #999999;
content: attr(placeholder);
}
input[type="date"] {
color: #ffffff;
}
input[type="date"]:focus,
input[type="date"]:valid {
color: #666666;
}
input[type="date"]:focus::before,
input[type="date"]:valid::before {
content: "" !important;
}
<input type="date" placeholder="Date" required>
I took jbarlow idea, but I added an if in the onblur function so the fields only change its type if the value is empty
<input placeholder="Date" class="textbox-n" type="text" onfocus="(this.type='date')" onblur="(this.value == '' ? this.type='text' : this.type='date')" id="date">
According to the HTML standard:
The following content attributes must not be specified and do not apply to the element: accept, alt, checked, dirname, formaction, formenctype, formmethod, formnovalidate, formtarget, height, inputmode, maxlength, minlength, multiple, pattern, placeholder, size, src, and width.
It works for me:
input[type='date']:after {
content: attr(placeholder)
}
I used this whit jQuery:
http://jsfiddle.net/daviderussoabram/65w1qhLz/
$('input[type="date"], input[type="datetime"], input[type="datetime-local"], input[type="month"], input[type="time"], input[type="week"]').each(function() {
var el = this, type = $(el).attr('type');
if ($(el).val() == '') $(el).attr('type', 'text');
$(el).focus(function() {
$(el).attr('type', type);
el.click();
});
$(el).blur(function() {
if ($(el).val() == '') $(el).attr('type', 'text');
});
});
Found a better way to solve your problem.
I think this will help you. when focused out, the box will change type into text so it will show your placeholder. when focused in, its type changes into date so the calendar view will be shown.
<input placeholder="Date" class="textbox-n" type="text" onfocusin="(this.type='date')" onfocusout="(this.type='text')" id="date">
<input placeholder="01-01-2021" class="textbox-n" type="text" onfocus="(this.type='date')" onblur="(this.type='text')" id="date" />
Adressing the problem in the current correct answer "clicking the field shows the onscreen keyboard instead of the datepicker":
The problem is caused by the Browser behaving according to the type of input when clicking (=text). Therefore it is necessary to stop from focussing on the input element (blur) and then restart focus programmatically on the input element which was defined as type=date by JS in the first step. Keyboard displays in phonenumber-mode.
<input placeholder="Date" type="text" onfocus="this.type='date';
this.setAttribute('onfocus','');this.blur();this.focus();">
To summarize the date inputs problem:
You have to display them (i.e. avoid display:none) otherwise the input UI will not be triggered ;
a placeholder is contradictory with them (as per the spec, and because they have to display a specific UI) ;
converting them to another input type for the unfocused time do allows placeholders, but focus then triggers the wrong input UI (keyboard), at least for a small time, because focus events cannot be cancelled.
inserting (before) or adding (after) content doesn't prevent the date input value to be displayed.
The solution I found to meet those requirements is to use the usual trick to style native form elements : ensure the element is displayed but not visible, and display its expected style through its associated label. Typically, the label will display as the input (including a placeholder), but over it.
So, an HTML like:
<div class="date-input>
<input id="myInput" type="date">
<label for="myInput">
<span class="place-holder">Enter a date</span>
</label>
</div>
Could be styled as:
.date-input {
display: inline-block;
position: relative;
}
/* Fields overriding */
input[type=date] + label {
position: absolute; /* Same origin as the input, to display over it */
background: white; /* Opaque background to hide the input behind */
left: 0; /* Start at same x coordinate */
}
/* Common input styling */
input[type=date], label {
/* Must share same size to display properly (focus, etc.) */
width: 15em;
height: 1em;
font-size: 1em;
}
Any event (click, focus) on such an associated label will be reflected on the field itself, and so trigger the date input UI.
Should you want to test such a solution live, you can run this Angular version from your tablet or mobile.
try my solution. I use 'required' attribute to get know whether input is filled and if not I show the text from attribute 'placeholder'
//HTML
<input required placeholder="Date" class="textbox-n" type="date" id="date">
//CSS
input[type="date"]:not(:valid):before {
content: attr(placeholder);
// style it like it real placeholder
}
Took me a while figuring this one out, leave it as type="text", and add onfocus="(this.type='date')", just as shown above.
I even like the onBlur idea mentioned above
<input placeholder="Date" class="textbox-n" type="text" onfocus="(this.type='date')" onblur="(this.type='text')" id="date">
Hope this helps anyone who didn't quite gather whats going on above
SO what i have decided to do finally is here and its working fine on all mobile browsers including iPhones and Androids.
$(document).ready(function(){
$('input[type="date"]').each(function(e) {
var $el = $(this),
$this_placeholder = $(this).closest('label').find('.custom-placeholder');
$el.on('change',function(){
if($el.val()){
$this_placeholder.text('');
}else {
$this_placeholder.text($el.attr('placeholder'));
}
});
});
});
label {
position: relative;
}
.custom-placeholder {
#font > .proxima-nova-light(26px,40px);
position: absolute;
top: 0;
left: 0;
z-index: 10;
color: #999;
}
<label>
<input type="date" placeholder="Date">
<span class="custom-placeholder">Date</span>
</label>
Date
Im working with ionicframework and solution provided by #Mumthezir is almost perfect. In case if somebody would have same problem as me(after change, input is still focused and when scrolling, value simply dissapears) So I added onchange to make input.blur()
<input placeholder="Date" class="textbox-n" type="text" onfocus=" (this.type='date')" onchange="this.blur();" id="date">
You can
set it as type text
convert to date on focus
make click on it
...let user check date
on change store the value
set input to type text
set text type input value to the stored value
like this...
$("#dateplaceholder").change(function(evt) {
var date = new Date($("#dateplaceholder").val());
$("#dateplaceholder").attr("type", "text");
$("#dateplaceholder").val(date.getDate() + "/" + (date.getMonth() + 1) + "/" + date.getFullYear());
});
$("#dateplaceholder").focus(function(evt) {
$("#dateplaceholder").attr("type", "date");
setTimeout('$("#dateplaceholder").click();', 500);
});
$("#dateplaceholder").attr("type", "text");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<input type="date" id="dateplaceholder" placeholder="Set the date" />
Found a better way to handle user basic comprehension with mouseover and opening datepicker on click :
<input type="text" onfocus="(this.type='date')" onmouseover="(this.type = 'date')" onblur="(this.value ? this.type = 'date' : this.type = 'text')" id="date_start" placeholder="Date">
Also hide webkit arrow and make it 100% wide to cover the click :
input[type="date"] {
position: relative;
}
input[type="date"]::-webkit-calendar-picker-indicator {
position: absolute;
height: 100%;
width: 100%;
opacity: 0;
left: 0;
right: 0;
top:0;
bottom: 0;
}
Expanding on #mvp's solution with unobtrusive javascript in mind, here's the approach:
HTML:
<input type="text" placeholder="Date" class="js-text-date-toggle">
Javascript:
$('.js-text-date-toggle').on('focus', function() {
$(this).attr('type', 'date');
}).on('blur', function() {
$(this).attr('type', 'text');
});
I think all you have to do is change the model to say the date field is nullable and then put [Required] on it if it is required. If you do this the placeholder text does appear.
Hey so I ran into the same issue last night and figured out a combination of all of your answer and some sparkling magic are doing a good job:
The HTML:
<input type="date" name="flb5" placeholder="Datum" class="datePickerPlaceHolder"/>
The CSS:
#media(max-width: 1024px) {
input.datePickerPlaceHolder:before {
color: #A5A5A5; //here you have to match the placeholder color of other inputs
content: attr(placeholder) !important;
}
}
The jQuery:
$(document).ready(function() {
$('input[type="date"]').change(function(){
if($(this).val().length < 1) {
$(this).addClass('datePickerPlaceHolder');
} else {
$(this).removeClass('datePickerPlaceHolder');
}
});
});
Explanation:
So, what is happening here, first of all in the HTML, this is pretty straight forward just doing a basic HMTL5-date-input and set a placeholder.
Next stop: CSS, we are setting a :before-pseudo-element to fake our placeholder, it just takes the placeholder's attribute from the input itself. I made this only available down from a viewport width of 1024px - why im going to tell later.
And now the jQuery, after refactoring a couple of times I came up with this bit of code which will check on every change if there is a value set or not, if its not it will (re-)add the class, vice-versa.
KNOW ISSUES:
there is a problem in chrome with its default date-picker, thats what the media-query is for. It will add the placeholder infront of the default 'dd.mm.yyyy'-thing. You could also set the placeholder of the date-input to 'date: ' and adjust the color incase of no value inside the input...for me this resulted in some other smaller issues so i went with just not showing it on 'bigger' screens
hope that helps!
cheerio!
From Angular point of view I managed to put a placeholder in input type date element.
First of all I defined the following css:
.placeholder {
color: $text-grey;
}
input[type='date']::before {
content: attr(placeholder);
}
input::-webkit-input-placeholder {
color: $text-grey;
}
The reason why this is neccessary is that if css3 content has different color that the normal placeholder, so I had to use a common one.
<input #birthDate
class="birthDate placeholder"
type="date"
formControlName="birthDate"
placeholder="{{getBirthDatePlaceholder() | translate}}"
[class.error]="!onboardingForm.controls.birthDate.valid && onboardingForm.controls.birthDate.dirty"
autocomplete="off"
>
Then in the template used a viewchild birthDate attribute, to be able to access this input from the component. And defined an angular expression on the placeholder attribute, which will decide if we show the placeholder or not. This is the major drawback of the solution, is that you have to manage the visibility of the placeholder.
#ViewChild('birthDate') birthDate;
getBirthDatePlaceholder() {
if (!this.birthDate) {
return;
} else {
return this.birthDate.nativeElement.value === '' ?
'ONBOARDING_FORM_COMPONENT.HINT_BIRTH_DATE' :
'';
}
}
<input placeholder="Date" type="text" onMouseOver="(this.type='date')" onMouseOut="(this.type='text')" id="date" class="form-control">
Revised code of mumthezir
If you're only concerned with mobile:
input[type="date"]:invalid:before{
color: rgb(117, 117, 117);
content: attr(placeholder);
}
I'm surprised there's only one answer with an approach similar to the one I used.
I got the inspiration from #Dtipson's comment on #Mumthezir VP's answer.
I use two inputs for this, one is a fake input with type="text" on which I set the placeholder, the other one is the real field with type="date".
On the mouseenter event on their container, I hide the fake input and show the real one, and I do the opposite on the mouseleave event. Obviously, I leave the real input visibile if it has a value set on it.
I wrote the code to use pure Javascript but if you use jQuery (I do) it's very easy to "convert" it.
// "isMobile" function taken from this reply:
// https://stackoverflow.com/a/20293441/3514976
function isMobile() {
try { document.createEvent("TouchEvent"); return true; }
catch(e) { return false; }
}
var deviceIsMobile = isMobile();
function mouseEnterListener(event) {
var realDate = this.querySelector('.real-date');
// if it has a value it's already visible.
if(!realDate.value) {
this.querySelector('.fake-date').style.display = 'none';
realDate.style.display = 'block';
}
}
function mouseLeaveListener(event) {
var realDate = this.querySelector('.real-date');
// hide it if it doesn't have focus (except
// on mobile devices) and has no value.
if((deviceIsMobile || document.activeElement !== realDate) && !realDate.value) {
realDate.style.display = 'none';
this.querySelector('.fake-date').style.display = 'block';
}
}
function fakeFieldActionListener(event) {
event.preventDefault();
this.parentElement.dispatchEvent(new Event('mouseenter'));
var realDate = this.parentElement.querySelector('.real-date');
// to open the datepicker on mobile devices
// I need to focus and then click on the field.
realDate.focus();
realDate.click();
}
var containers = document.getElementsByClassName('date-container');
for(var i = 0; i < containers.length; ++i) {
var container = containers[i];
container.addEventListener('mouseenter', mouseEnterListener);
container.addEventListener('mouseleave', mouseLeaveListener);
var fakeDate = container.querySelector('.fake-date');
// for mobile devices, clicking (tapping)
// on the fake input must show the real one.
fakeDate.addEventListener('click', fakeFieldActionListener);
// let's also listen to the "focus" event
// in case it's selected using a keyboard.
fakeDate.addEventListener('focus', fakeFieldActionListener);
var realDate = container.querySelector('.real-date');
// trigger the "mouseleave" event on the
// container when the value changes.
realDate.addEventListener('change', function() {
container.dispatchEvent(new Event('mouseleave'));
});
// also trigger the "mouseleave" event on
// the container when the input loses focus.
realDate.addEventListener('blur', function() {
container.dispatchEvent(new Event('mouseleave'));
});
}
.real-date {
display: none;
}
/* a simple example of css to make
them look like it's the same element */
.real-date,
.fake-date {
width: 200px;
height: 20px;
padding: 0px;
}
<div class="date-container">
<input type="text" class="fake-date" placeholder="Insert date">
<input type="date" class="real-date">
</div>
I tested this also on an Android phone and it works, when the user taps on the field the datepicker is shown. The only thing is, if the real input had no value and the user closes the datepicker without choosing a date, the input will remain visible until they tap outside of it. There's no event to listen to to know when the datepicker closes so I don't know how to solve that.
I don't have an iOS device to test it on.
This might help in some situation.
<input type="text" id="date" onclick="this.type='date'" onblur="this.type='text'" placeholder="Date" class="textbox-n" name="myDate" />
HTML:
<div>
<input class="ui-btn ui-btn-icon-right ui-corner-all ui-icon-calendar ui-shadow" id="inputDate" type="date"/>
<h3 id="placeholder-inputDate">Date Text</h3>
</div>
JavaScript:
$('#inputDate').ready(function () {
$('#placeholder-inputDate').attr('style'
, 'top: ' + ($('#placeholder-inputDate').parent().position().top + 10)
+ 'px; left: ' + ($('#placeholder-inputDate').parent().position().left + 0) + 'px; position: absolute;');
$('#inputDate').attr('style'
, 'width: ' + ($('#placeholder-inputDate').width() + 32) + 'px;');
});
Here is another possible hack not using js and still using css content. Note that as :after is not supported on some browser for inputs, we need to select the input in another way, same for content attr('')
input[type=date]:invalid+span:after {
content:"Birthday";
position:absolute;
left:0;
top:0;
}
input[type=date]:focus:invalid+span:after {
display:none;
}
input:not(:focus):invalid {
color:transparent;
}
label.wrapper {
position:relative;
}
<label class="wrapper">
<input
type="date"
required="required"
/>
<span></span>
</label>

Delay HTML5 :invalid pseudo-class until the first event

I recently discovered that the :invalid pseudo-class applies to required form elements as soon as the page loads. For example, if you have this code:
<style>
input:invalid { background-color: pink; color: white; }
input:valid { background-color: white; color: black; }
</style>
…
<input name="foo" required />
Then your page will load with an empty pink input element on it. Having validation built in to HTML5 is great, but I don't think most users expect the form to validate before they've had a chance to enter any values at all. Is there any way to delay the application of the pseudo-class until the first event affecting that element (form submit, blur, change, whatever's appropriate)? Is it possible to do this without JavaScript?
http://www.alistapart.com/articles/forward-thinking-form-validation/
Since we only want to denote that a field is invalid once it has
focus, we use the focus pseudo-class to trigger the invalid styling.
(Naturally, flagging all required fields as invalid from the start
would be a poor design choice.)
Following this logic, your code would look something like this...
<style>
input:focus:required:invalid {background-color: pink; color: white;}
input:required:valid {background-color: white; color: black; }
</style>
Created a fiddle here: http://jsfiddle.net/tbERP/
As you'd guess, and as you'll see from the fiddle, this technique only shows the validation styling when the element has focus. As soon as you move focus off, the styling is dropped, regardless of whether it is valid or not. Not ideal by any means.
These answers are out of date. Now you can do this by checking for a placeholder pseudo-class with CSS.
input:not(:placeholder-shown):invalid {
background-color: salmon;
}
form:invalid button {
background-color: salmon;
pointer-events: none;
}
<form>
<input type="email" placeholder="me#example.com" required>
<button type="submit">Submit</button>
</form>
It starts with a normal background and turns pink as you enter you incomplete email address into it.
This is not possible in pure CSS, but can be done with JavaScript. This is a jQuery example:
// use $.fn.one here to fire the event only once.
$(':required').one('blur keydown', function() {
console.log('touched', this);
$(this).addClass('touched');
});
/**
* All required inputs initially are yellow.
*/
:required {
background-color: lightyellow;
}
/**
* If a required input has been touched and is valid, it should be white.
*/
.touched:required:valid {
background-color: white;
}
/**
* If a required input has been touched and is invalid, it should be pink.
*/
.touched:required:invalid {
background-color: pink;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>
<label>
Name:
<input type="text" required> *required
</label>
</p>
<p>
<label>Age:
<input type="text">
</label>
</p>
This is a VanillaJS (no jQuery) version of kzh's answer
{
let f = function() {
this.classList.add('touched')
}
document
.querySelectorAll('input')
.forEach((e) => {
e.addEventListener('blur', f, false)
e.addEventListener('keydown', f, false)
})
}
/**
* All required inputs initially are yellow.
*/
:required {
background-color: lightyellow;
}
/**
* If a required input has been touched and is valid, it should be white.
*/
.touched:required:valid {
background-color: white;
}
/**
* If a required input has been touched and is invalid, it should be pink.
*/
.touched:required:invalid {
background-color: pink;
}
<p><label>
Name:
<input type="text" required> *required
</label></p>
<p><label>Age:
<input type="text">
</label></p>
Mozilla takes care of this with its own :-moz-ui-invalid pseudoclass that only applies to forms after they've been interacted with. MDN does not recommend using this due to a lack of support. However, you can modify it for Firefox.
There's a level 4 spec for a :user-invalid spec on the horizon that will offer similar behavior.
I created a small shim to deal with this in my codebase. I just start off with my <form/> element having the novalidate property along with a data-validate-on="blur" attribute. This watches for the first event of that type. This way you can still use the native :invalid css selectors for the form styling.
$(function () {
$('[data-validate-on]').each(function () {
var $form = $(this);
var event_name = $form.data('validate-on');
$form.one(event_name, ':input', function (event) {
$form.removeAttr('novalidate');
});
});
});
There is a html5 invalid event that fires on form elements before the submit event occurs for each element that does not pass checkValidity. You can use this event to apply a class for example to the surrounding form and display :invalid styles only after this event occurs.
$("form input, form select, form textarea").on("invalid", function() {
$(this).closest('form').addClass('invalid');
});
Your CSS would then look something like this:
:invalid { box-shadow: none; }
.invalid input:invalid,
.invalid textarea:invalid,
.invalid select:invalid { border: 1px solid #A90909 !important; background-color: #EEC2C2; }
The first line removes the default styling, so form elements look neutral at page load. As soon as the invalid event fires (when a user tries to submit the form), the elements are visibly rendered invalid.
You could make it so that only elements that have a certain class on them and are required, are pink. Add an event handler to each required element that adds that class when you leave the element.
Something like:
<style>
input.touched:invalid { background-color: pink; color: white; }
input.touched:valid { background-color: white; color: black; }
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
var required = document.querySelectorAll('input:required');
for (var i = 0; i < required.length; ++i) {
(function(elem) {
function removeClass(name) {
if (elem.classList) elem.classList.remove(name);
else
elem.className = elem.className.replace(
RegExp('(^|\\s)\\s*' + name + '(?:\\s+|$)'),
function (match, leading) {return leading;}
);
}
function addClass(name) {
removeClass(name);
if (elem.classList) elem.classList.add(name);
else elem.className += ' ' + name;
}
// If you require a class, and you use JS to add it, you end up
// not showing pink at all if JS is disabled.
// One workaround is to have the class on all your elements anyway,
// and remove it when you set up proper validation.
// The main problem with that is that without JS, you see what you're
// already seeing, and stuff looks hideous.
// Unfortunately, you kinda have to pick one or the other.
// Let non-blank elements stay "touched", if they are already,
// so other stuff can make the element :invalid if need be
if (elem.value == '') addClass('touched');
elem.addEventListener('blur', function() {
addClass('touched');
});
// Oh, and when the form submits, they need to know about everything
if (elem.form) {
elem.form.addEventListener('submit', function() {
addClass('touched');
});
};
})(required[i]);
}
});
</script>
And of course, it won't work as is in IE8 or below, as (a) DOMContentLoaded is relatively new and wasn't standard when IE8 came out, (b) IE8 uses attachEvent rather than the DOM-standard addEventListener, and (c) IE8 isn't going to care about :required anyway, as it doesn't technically support HTML 5.
While using HTML5 form validation, try to use the browser to detect for invalid submissions/fields, rather than re-inventing the wheel.
Listen for the invalid event to add a class of 'invalid' to your form. With the 'invalid' class added, you can go to town with styling your form using CSS3 :pseudo selectors.
For example:
// where myformid is the ID of your form
var myForm = document.forms.myformid;
var checkCustomValidity = function(field, msg) {
if('setCustomValidity' in field) {
field.setCustomValidity(msg);
} else {
field.validationMessage = msg;
}
};
var validateForm = function() {
// here, we're testing the field with an ID of 'name'
checkCustomValidity(myForm.name, '');
if(myForm.name.value.length < 4) {
checkCustomValidity(
// alerts fields error message response
myForm.name, 'Please enter a valid Full Name, here.'
);
}
};
/* here, we are handling your question above, by adding an invalid
class to the form if it returns invalid. Below, you'll notice
our attached listener for a form state of invalid */
var styleInvalidForm = function() {
myForm.className = myForm.className += ' invalid';
}
myForm.addEventListener('input', validateForm, false);
myForm.addEventListener('keyup', validateForm, false);
myForm.addEventListener('invalid', styleInvalidForm, true);
Now, simply style your form as you see fit based on the 'invalid' class we've attached.
For example:
form.invalid input:invalid,
form.invalid textarea:invalid {
background: rgba(255, 0, 0, .05);
border-color: #ff6d6d;
-webkit-box-shadow: 0 0 6px rgba(255, 0, 0, .35);
box-shadow: 0 0 6px rgba(255, 0, 0, .35);
}
A good way is to abstract :invalid, :valid with a CSS classes and then some JavaScript to check if the input field was focused or not.
CSS:
input.dirty:invalid{ color: red; }
input.dirty:valid{ color: green; }
JS:
// Function to add class to target element
function makeDirty(e){
e.target.classList.toggle('dirty');
}
// get form inputs
var inputs = document.forms[0].elements;
// bind events to all inputs
for(let input of inputs){
input.addEventListener('invalid', makeDirty);
input.addEventListener('blur', makeDirty);
input.addEventListener('valid', makeDirty);
}
DEMO
Following on from agouseh's idea, you can have a bit of javascript to tell when the submit button has been focussed, and have validation show up at that time.
The javascript will add a class (eg. submit-focussed) to the form field when the submit button is focussed or clicked, which then allows the CSS to style invalid inputs.
This follows the best practice of showing validation feedback after the user has finished filling in the fields, as according to research there is no additional benefit to showing it during the process.
document
.querySelector('input[type=submit]')
.onfocus = function() {
this
.closest('form')
.classList
.add('submit-focussed');
};
form.submit-focussed input:invalid {
border: thin solid red;
}
<form>
<label>Email <input type="email" required="" /></label>
<input type="submit" />
</form>
jQuery alternative
(function($) {
$('input[type=submit]').on('focus', function() {
$(this)
.parent('form')
.addClass('submit-focussed');
});
})(jQuery); /* WordPress compatible */
Here is my method to avoid the default styling of any unfocused input as invalid, you just have to add a simple js command onFocus to let the webpage to identify focused and unfocused inputs, so all the input will not appear in the style of invalid at first place.
<style>
input.focused:required:invalid { background-color: pink; color: white; }
input:valid { background-color: white; color: black; }
</style>
…
<input name="foo" class="notfocused" onFocus="document.activeElement.className='focused';" required />
Try it yourself below:
input.focused:required:invalid {
background-color: pink;
color: white;
}
input:required:valid {
background-color: darkseagreen;
color: black;
}
<label>At least 1 charater:</label><br />
<input type="text" name="foo" class="notfocused" onFocus="document.activeElement.className='focused';" required />
I can't comment, but to go with #Carl's very useful answer regarding using :not(:placeholder-shown). As another comment mentioned, this will still show the invalid state if you have NO placeholder (as some form designs call for).
To solve this, simply add an empty placeholder like so
<input type="text" name="username" placeholder=" " required>
Then your CSS, something like
:not(:placeholder-shown):invalid{ background-color: #ff000038; }
Worked for me!