Make custom checkbox switch Label element a11y tab / keyboard accessible - html

So I'm trying to make custom slider that's cross browser (IE11, Chrome, Firefox, Edge) that's also a11y accessible and I'm having trouble getting the label element to respond to space bar for selection like a natural checkbox. I can do it easily with code of course but was curious if there's maybe something I'm missing with just the html/css I could do to accomplish the same thing.
As example see below, tabindex of course provides the visual and tabbing, but I can't seem to get the label to toggle it like a click would on label element. Should I just go the easy route and let some code handle it or does someone want to teach me something? Cheers!
// not yet, and yes I know I haven't added the aria attributes etc, it's just a quick PoC :)
main {
display: block;
margin: 0 auto;
padding: 2rem;
width: auto;
}
.slide-toggle {
margin: 0 3rem;
display: inline-block;
}
.slide-toggle:last-of-type {
margin-left: 0;
}
.slide-toggle input {
display: none;
}
.slide-toggle input:checked ~ label {
color: #fff;
background-color: skyblue;
}
.slide-toggle input:checked ~ label:after {
transform: translateX(100%);
}
.slide-toggle label {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
position: relative;
padding: .25rem 0;
cursor: pointer;
user-select: none;
border: #bbb 1px solid;
border-radius: 3px;
}
.slide-toggle label:after {
display: block;
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 45%;
margin: .25rem;
border: #bbb 1px solid;
border-radius: 3px;
background-color: #ddd;
transition: transform 0.25s cubic-bezier(0.6, 0.82, 0, 0.76);
}
.slide-toggle label:focus {
box-shadow: 0 0 0 8px red;
}
.slide-toggle label:hover {
border-color: #777;
}
.slide-toggle label div {
flex-grow: 1;
flex-basis: 50%;
flex-wrap: nowrap;
text-align: center;
min-width: 1rem;
margin: .25rem 1rem;
}
<link href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" rel="stylesheet"/>
<main>
<h2>Please click a slide toggle for example;</h2>
<div class="slide-toggle">
<input id="guidLater"
type="checkbox"/>
<label for="guidLater"
tabindex="0"
role="checkbox">
<div>YES</div>
<div>NO</div>
</label>
</div>
No Translation Option:
<div class="slide-toggle">
<input id="guidLater4"
type="checkbox"/>
<label for="guidLater4"
tabindex="0"
role="checkbox">
<div><i class="fas fa-check" style="color:green"></i></div>
<div><i class="fas fa-times" style="color:red;"></i></div>
</label>
</div>
</main>

Without getting too far into the specifics, as there are many, I'll just attack this from a pure solution perspective and provide some insight.
With HTML5 elements, the idea is that they have functionality assigned to them by default. For example, a checkbox inherits the behaviors that a checkbox should have because it's a default element. You also shouldn't be re-purposing elements for other uses if native ones are available as this breaks the first two rules of ARIA.
If you can use a native HTML element [HTML51] or attribute with the semantics and behavior you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so.
And
Do not change native semantics, unless you really have to.
The input types of Range (a slider) and Checkbox have two vastly different sets of keyboard behaviors, so i'm not surprised that this isn't working correctly.

I think what you mean to create is a "switch"
You shouldn't have display: none on the actual checkbox. You need to visually hide it, but still have it on the page because only then it would catch the space key press.
An easy (& suggested) way to do that is
.slide-toggle input {
width: 0;
height: 0;
}
also you don't need to have role="checkbox" and tabindex on the label.

Related

CSS Custom checkboxes in grid - can't remove original checkbox

I have some checkboxes in a grid and want to use vanilla-css and html to make a custom checkbox. That works fine. The problem is the remaining box of the original checkbox, that stays in my grid and makes it behave in strange ways as it takes a cell. Even when I make it transparent or deactivate it, as it is often suggested.
In the original example they moved it out of the screen area, but I can make it escape the grid.
I think this is the part where it fails to behave like I want to:
[type="checkbox"]:not(:checked),
[type="checkbox"]:checked {
position: absolute;
left: -9999px;
}
Here is a minimal example: https://jsfiddle.net/3mzsLj1v/14/
Here is the example I used: https://css-tricks.com/the-checkbox-hack/
Here is the real code I work on: https://codepen.io/vaeng/pen/XWXKoMb
Thanks for your help. I am sure this is very common, but being a beginner, I might not use css in the correct way?
In both your "minimal" and "real code" examples, your "New Checkboxes" comments are not properly opened.
In minimal example:
Line 23: *New Checkboxes*/ s/b /*New Checkboxes*/
In real code example:
Line 123: * New Checkboxes and radio buttons*/ s/b /* New Checkboxes and radio buttons*/
If you fix these lines, your code should work as intended.
Also, I noticed in line 102 that you put // before visibility: hidden;. If you want to comment this line, this syntax is not valid in CSS.
You see, your label and input element are on the same level, and even with position: absolute; your input still a part of the grid. Replaced your input inside the label, added span element and rewrited CSS.
Although in your code was
* New Checkboxes*/
/* Base for label styling */
[type="checkbox"]:not(:checked),
[type="checkbox"]:checked {
position: absolute;
left: -9999px;
}
The first comment was closed incorrect, so next statement didn't work.
.body {
height: 100%;
}
.outer-box {
display: grid;
margin: auto;
background-color: green;
width: 300px;
align-self: center;
grid-gap: 10px;
}
.inner-box {
display: grid;
width: 80%;
background-color: red;
align-self: center;
margin: 5px 5px 5px 5px;
}
/* New Checkboxes*/
/* Base for label styling */
[type="checkbox"] {
position: absolute;
left: -9999px;
}
[type="checkbox"]:not(:checked)+span,
[type="checkbox"]:checked+span {
position: relative;
padding-left: 1.95em;
cursor: pointer;
}
/* checkbox aspect */
[type="checkbox"]:not(:checked)+span:before,
[type="checkbox"]:checked+span:before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 1em;
height: 1em;
border: 1px solid grey;
background: transparent;
}
/* checked mark aspect */
[type="checkbox"]:not(:checked)+span:after,
[type="checkbox"]:checked+span:after {
content: '\2713\0020';
position: absolute;
top: .05em;
left: .2em;
font-size: 1.3em;
line-height: 0.8;
color: whitesmoke;
transition: all .2s;
font-family: Arial;
}
/* checked mark aspect changes */
[type="checkbox"]:not(:checked)+span:after {
opacity: 0;
transform: scale(0);
}
[type="checkbox"]:checked+span:after {
opacity: 1;
transform: scale(1);
}
<div class="outer-box">
<div class="inner-box">
Some text
</div>
<div class="inner-box">
<label for="box1" class="container">
<input type="checkbox" id="box1"><span>Selectbox1</span>
</label>
<label for="box2" class="container">
<input type="checkbox" id="box2"><span>Selectbox2</span>
</label>
</div>
</div>
And please, don't use display: grid; for every element. It's very specific setting only for cases, when you really need you use grid.

Custom checkbox wont toggle when click and drag on label

I have a problem with my custom checkbox.
If you click on a checkbox element, move the mouse cursor and then release the click inside the checkbox area, the checkbox is checked.
However, if you do the same on a custom checkbox (here, a div inside a label), the checkbox isn't checked.
It's a problem because if you want to quickly check a checkbox, you may move the mouse after pressing the button of the mouse and before releasing it, thus not toggling the checkbox.
The user is obligated to click without moving the mouse.
I know I can use JS to emulate a checkbox with a div, but I want the HTML to be semantically correct, so: Is it possible to fix it without js?
Here's the code :
/* 1. Hide the checkbox */
.hidden {
/* https://zellwk.com/blog/hide-content-accessibly/ */
border: 0;
clip: rect(0, 0, 0, 0);
height: auto;
margin: 0;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
white-space: nowrap;
}
/* 2. Use a label to retrieve the click event */
label {
/* not used directly to prevent the bug in firefox https://bugzilla.mozilla.org/show_bug.cgi?id=608180 */
pointer-events: none;
display: inline-flex;
}
label > input {
/*usefull for testing only*/
pointer-events : all;
}
label > .customCheckbox {
cursor: pointer;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
--size:200px;
width: var(--size);
height: var(--size);
font-size: calc(var(--size)/2);
pointer-events: all;
}
label > .customCheckbox::selection{
/* prevent highliting the text within the custom checkbox */
background: none;
}
label > .customCheckbox:after {
z-index: -1;
content: "✔";
display: inline-flex;
justify-content: center;
align-items: center;
position: absolute;
width: 100%;
height: 100%;
box-sizing: border-box;
border-radius: calc(var(--size)/4);
border: solid black calc(var(--size)/10);
}
label > input:not(:checked) + div.customCheckbox:after {
background:#0000;
content: "\a0";
}
<input type="checkbox"/>
<label >
<input type="checkbox" class="hidden"/>
<div class="customCheckbox"></div>
</label >
I need the checkbox to be inside the label because I can't use the "for" attribute.
Thanks a lot for your help!
EDIT: for those wondering, here's the js solution (not ideal but since I can't do it in CSS, it's better than nothing) :
let checkboxes = document.getElementsByClassName("customCheckbox");
for(let i=0; i<checkboxes.length; i++){
checkboxes[i].addEventListener('mouseover', (e)=>{handleMouseOverCheckbox(e)})
checkboxes[i].addEventListener('mousedown', (e)=>{handleMouseOverCheckbox(e)})
}
function handleMouseOverCheckbox(e) {
e.srcElement.previousElementSibling.disabled = "true";
if (e.buttons == 1) {
e.srcElement.previousElementSibling.checked= !e.srcElement.previousElementSibling.checked;
}}
EDIT 2 : Here's the best solution I could come up with, thanks to
#zer00ne
codepen.io/DesignThinkerer/pen/bGVBLjM
A checkbox within a label is no problem. The problem arises when that checkbox is altered for the sake of accessibility instead of complete removal using display: none. When an interactive element like an input or button exists in the DOM, it will still be a factor no matter how its hidden unless display: none is applied.
In the Original Post the checkbox is almost impossible to click due to its 0px height and 1px width and yet when the div is clicked, the checkbox is clicked... sometimes not. Normally if the label was able to detect a click, that click would trigger a click event to the nested checkbox as well. In the OP code that's not happening because the label has pointer-events: none.
So the div is getting clicked and by some magical miracle this gains features that would not normally be attributed to it? Divs are not interactive they cannot affect elements that are not nested within themselves (i.e. like the checkbox that sits before the div). Nope, the div is useless, its the checkbox itself that's getting clicked due to the fact it is the only element within the inert label that gains focus by default. Gaining focus on an input doesn't necessarily guarantee a click event -- matter of fact a focus event selects an element and a click event sets an element as active. So what happens when a user double-clicks or moves the mouse quickly before the next click clears a label? Undesirable behavior as described in OP.
In the following demo, the checkboxes are hidden as per OP (also set width and height to 0) and removed pointer-events: none from the label and added it to the checkboxes. In this setup the label gains focus and click events and the click event will trigger the checkbox. The checkbox having been isolated from any extra clicks due to pointer-events: none and z-index: -1 should behave as expected.
As proof of concept I have added some JavaScript to demonstrate said code stability. The two event handlers are for demonstration purposes. The JS does not facilitate, stabilize, or modify performance of the HTML/CSS behavior.
On any change event on a checkbox (via label) will trigger function changeHandler() to gather all the values of the checked checkboxes and display them in an output.
If there's a checkmark in a box and there's a value displayed that corresponds to said checked checkbox, then it successfully passes as valid behavior.
Clicking button.show will trigger function clickHandler() to toggle the .reveal class to each checkbox.
While clicking rapidly observe that the revealed checkboxes are checked and its corresponding custom label is checked as well. Also notice that the value should also be displayed as well.
BTW
"....target doesn't work in IE IIRC"
event.target is the standard property to use in every modern browser. event.srcElement is a deprecated property used by IE which is almost entirely unsupported.
pointer-events: all assigned to input and .customCheckbox
The value all applies to SVG only. Only the values of none and auto are relevant to HTML. auto is default.
Demo
I cannot reproduce the described behavior except in the code provided in OP. If you can reproduce that behavior on my demo, please record a short video of it and post that and the machine/device, OS, and browsers (I will assume everything is reasonably up to date).
const main = document.forms.main;
main.onchange = checkHandler;
function checkHandler(e) {
const fc = this.elements;
const chx = [...fc.hidden];
const ui = e.target;
if (ui.matches('.hidden')) {
let text = chx.flatMap(c => c.checked ? [c.value] : []);
fc.view.value = '';
fc.view.value = text.join(', ');
}
}
main.onclick = clickHandler
function clickHandler(e) {
const fc = this.elements;
const chx = [...fc.hidden];
const ui = e.target;
if (ui.matches('button.show')) {
chx.forEach(c => c.classList.toggle('reveal'));
}
}
:root,
body {
--size: 10rem;
font: 400 small-caps 2vw/1.5 Times;
width: 100%;
height: 100%;
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
font: inherit;
}
.display {
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
align-items: center;
width: max-content;
max-height: min-content;
margin: 10px;
border: calc(var(--size) / 20) solid #000;
border-radius: 24px;
}
.view {
display: inline-block;
min-width: 35ch;
font-size: 1.5rem;
height: 1.5rem;
line-height: 1;
}
.show {
display: inline-block;
width: 12ch;
padding: 1px 3px;
margin: 4px;
border: 2px solid #000;
border-radius: 8px;
background: none;
text-align: center;
font-size: 1.25rem;
cursor: pointer;
}
.mask {
position: relative;
z-index: 2;
display: block;
width: var(--size);
height: var(--size);
padding: 0;
margin: 0 5px;
border: solid black calc(var(--size) / 10);
border-radius: calc(var(--size) / 4);
font-size: calc(var(--size) * 0.8);
background: none;
cursor: pointer;
}
.icon {
position: absolute;
z-index: 1;
top: -1.5rem;
right: 1rem;
display: inline-block;
margin: 0;
padding: 0;
border: 0;
}
.hidden {
position: relative;
z-index: -1;
display: inline-block;
width: 0;
height: 0;
margin: 0;
padding: 0;
border: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
pointer-events: none;
opacity: 0;
}
.reveal {
z-index: 0;
top: -24px;
left: 4px;
width: 1px;
height: 1px;
opacity: 1;
}
.icon::after {
content: attr(data-blank);
}
.hidden:checked+.icon::after {
content: attr(data-check);
}
<!DOCTYPE html>
<html>
<head lang='en'>
<meta charset='utf-8'>
<style></style>
</head>
<body>
<form name='main'>
<fieldset name='display' class='display'>
<output name='view' class='view'></output>
<button name='show' class='show' type='button'>Show</button>
</fieldset>
<fieldset name='display' class='display'>
<label name='mask' class='mask'>
<input name='hidden' class="hidden" type="checkbox" value='Check I'>
<fieldset name='icon' class='icon' data-check='✔' data-blank=' '></fieldset>
</label>
<label name='mask' class='mask'>
<input name='hidden' class="hidden" type="checkbox" value='Check II'>
<fieldset name='icon' class='icon' data-check='✔' data-blank=' '></fieldset>
</label>
<label name='mask' class='mask'>
<input name='hidden' class="hidden" type="checkbox" value='Check III'>
<fieldset name='icon' class='icon' data-check='✔' data-blank=' '></fieldset>
</label>
<label name='mask' class='mask'>
<input name='hidden' class="hidden" type="checkbox" value='Check IV'>
<fieldset name='icon' class='icon' data-check='✔' data-blank=' '></fieldset>
</label>
</fieldset>
</form>
<script></script>
</body>
</html>

Issue with CSS Transition and the Label of a Checkbox

I want to change the position of my label when the checkbox is checked. This works fine if I don't transition the top offset property of my label. However, if I add a transition to this value (see the commented code), click on the label and don't move the cursor of my mouse the label seems that is still on hover state. That means, even though I don't hover on it, the cursor is a pointer and the background-color green (hover state of label).
If you see the demo, you'll understand what I mean.
My HTML code is the following:
<section>
<input type="checkbox" id="checkbox_id">
<label for="checkbox_id">Click me</label>
<div>
This is the first link
This is the second link
</div>
</section>
My CSS:
* {
margin: 0;
}
section {
position: relative;
padding: 20px;
background: yellow;
}
input[type="checkbox"] {
display: none;
}
label, a {
height: 30px;
padding: 10px 0;
margin: 10px;
}
label {
display: block;
background: tomato;
cursor: pointer;
width: 100%;
position: absolute;
top: 0;
/*transition: top .3s ease;*/
}
label:hover {
background: green;
}
input[type="checkbox"]:checked + label {
top: 100%;
}
a {
display: block;
background: tomato;
}
a:first-child {
margin-top: 50px;
}
Any idea why that's happening?
So, a little bit of jQuery might help us out here. Take a look at this jsFiddle.
CSS change:
.label--hovered { background: green; }
instead of:
label:hover { background: green; }
i.e. converted it to a class.
JavaScript:
$(document).ready(function(){
$('label').hover(function(){
$(this).removeAttr('style').addClass('label--hovered');
}, function(){
$(this).css('cursor', 'default').removeClass('label--hovered');
}).click(function(){
$(this).trigger('mouseleave');
});
});
Does this help?
You are using the syntax for the transition property but on transform. Additionally, transform doesn't take a top value. It takes scale, rotation, skew etc. You probably want this:
transition: top .3s ease;
Also don't forget to add vendor prefixes. (ie. webkit).

Cross-browser custom styling for file upload button [duplicate]

This question already has answers here:
How to customize <input type="file">?
(18 answers)
Closed 6 years ago.
I'm trying to style a file upload button to my personal preferences, but I couldn't find any really solid ways to do this without JS. I did find two other questions about this subject, but the answers there either involved JavaScript, or suggested Quirksmode's approach.
My major issue with this Quirksmode's approach is that the file button will still have the browser-defined dimensions, so it won't automatically adjust to whatever's used as button that's placed below it. I've made some code, based on it, but it will just take up the space the file button would normally take up, so it won't at all fill the parent div like I want it to.
HTML:
<div class="myLabel">
<input type="file"/>
<span>My Label</span>
</div>
CSS:
.myLabel {
position: relative;
}
.myLabel input {
position: absolute;
z-index: 2;
opacity: 0;
width: 100%;
height: 100%;
}
This fiddle demonstrates how this approach is quite flawed. In Chrome, clicking the !! below the second demo button will open the file dialog anyway, but also in all other browsers, the file button doesn't take up the correct areas of the button.
Is there any more solid way to style the file upload button, without any JavaScript, and preferably using as little 'hacky' coding as possible (since hacking usually brings other problems along with it, such as the ones in the fiddle)?
I'm posting this because (to my surprise) there was no other place I could find that recommended this.
There's a really easy way to do this, without restricting you to browser-defined input dimensions. Just use the <label> tag around a hidden file upload button. This allows for even more freedom in styling than the styling allowed via webkit's built-in styling[1].
The label tag was made for the exact purpose of directing any click events on it to the child inputs[2], so using that, you won't require any JavaScript to direct the click event to the input button for you anymore. You'd to use something like the following:
label.myLabel input[type="file"] {
position:absolute;
top: -1000px;
}
/***** Example custom styling *****/
.myLabel {
border: 2px solid #AAA;
border-radius: 4px;
padding: 2px 5px;
margin: 2px;
background: #DDD;
display: inline-block;
}
.myLabel:hover {
background: #CCC;
}
.myLabel:active {
background: #CCF;
}
.myLabel :invalid + span {
color: #A44;
}
.myLabel :valid + span {
color: #4A4;
}
<label class="myLabel">
<input type="file" required/>
<span>My Label</span>
</label>
I've used a fixed position to hide the input, to make it work even in ancient versions of Internet Explorer (emulated IE8- refused to work on a visibility:hidden or display:none file-input). I've tested in emulated IE7 and up, and it worked perfectly.
You can't use <button>s inside <label> tags unfortunately, so you'll have to define the styles for the buttons yourself. To me, this is the only downside to this approach.
If the for attribute is defined, its value is used to trigger the input with the same id as the for attribute on the <label>.
Please find below a way that works on all browsers. Basically I put the input on top the image.
I make it huge using font-size so the user is always clicking the upload button.
.myFile {
position: relative;
overflow: hidden;
float: left;
clear: left;
}
.myFile input[type="file"] {
display: block;
position: absolute;
top: 0;
right: 0;
opacity: 0;
font-size: 100px;
filter: alpha(opacity=0);
cursor: pointer;
}
<label class="myFile">
<img src="http://wscont1.apps.microsoft.com/winstore/1x/c37a9d99-6698-4339-acf3-c01daa75fb65/Icon.13385.png" alt="" />
<input type="file" />
</label>
The best example is this one, No hiding, No jQuery, It's completely pure CSS
http://css-tricks.com/snippets/css/custom-file-input-styling-webkitblink/
.custom-file-input::-webkit-file-upload-button {
visibility: hidden;
}
.custom-file-input::before {
content: 'Select some files';
display: inline-block;
background: -webkit-linear-gradient(top, #f9f9f9, #e3e3e3);
border: 1px solid #999;
border-radius: 3px;
padding: 5px 8px;
outline: none;
white-space: nowrap;
-webkit-user-select: none;
cursor: pointer;
text-shadow: 1px 1px #fff;
font-weight: 700;
font-size: 10pt;
}
.custom-file-input:hover::before {
border-color: black;
}
.custom-file-input:active::before {
background: -webkit-linear-gradient(top, #e3e3e3, #f9f9f9);
}
<input type="file" class="custom-file-input">
This seems to take care of business pretty well. A fidde is here:
HTML
<label for="upload-file">A proper input label</label>
<div class="upload-button">
<div class="upload-cover">
Upload text or whatevers
</div>
<!-- this is later in the source so it'll be "on top" -->
<input name="upload-file" type="file" />
</div> <!-- .upload-button -->
CSS
/* first things first - get your box-model straight*/
*, *:before, *:after {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
label {
/* just positioning */
float: left;
margin-bottom: .5em;
}
.upload-button {
/* key */
position: relative;
overflow: hidden;
/* just positioning */
float: left;
clear: left;
}
.upload-cover {
/* basically just style this however you want - the overlaying file upload should spread out and fill whatever you turn this into */
background-color: gray;
text-align: center;
padding: .5em 1em;
border-radius: 2em;
border: 5px solid rgba(0,0,0,.1);
cursor: pointer;
}
.upload-button input[type="file"] {
display: block;
position: absolute;
top: 0; left: 0;
margin-left: -75px; /* gets that button with no-pointer-cursor off to the left and out of the way */
width: 200%; /* over compensates for the above - I would use calc or sass math if not here*/
height: 100%;
opacity: .2; /* left this here so you could see. Make it 0 */
cursor: pointer;
border: 1px solid red;
}
.upload-button:hover .upload-cover {
background-color: #f06;
}
Any easy way to cover ALL file inputs is to just style your input[type=button] and drop this in globally to turn file inputs into buttons:
$(document).ready(function() {
$("input[type=file]").each(function () {
var thisInput$ = $(this);
var newElement = $("<input type='button' value='Choose File' />");
newElement.click(function() {
thisInput$.click();
});
thisInput$.after(newElement);
thisInput$.hide();
});
});
Here's some sample button CSS that I got from http://cssdeck.com/labs/beautiful-flat-buttons:
input[type=button] {
position: relative;
vertical-align: top;
width: 100%;
height: 60px;
padding: 0;
font-size: 22px;
color:white;
text-align: center;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
background: #454545;
border: 0;
border-bottom: 2px solid #2f2e2e;
cursor: pointer;
-webkit-box-shadow: inset 0 -2px #2f2e2e;
box-shadow: inset 0 -2px #2f2e2e;
}
input[type=button]:active {
top: 1px;
outline: none;
-webkit-box-shadow: none;
box-shadow: none;
}
I just came across this problem and have written a solution for those of you who are using Angular. You can write a custom directive composed of a container, a button, and an input element with type file. With CSS you then place the input over the custom button but with opacity 0. You set the containers height and width to exactly the offset width and height of the button and the input's height and width to 100% of the container.
the directive
angular.module('myCoolApp')
.directive('fileButton', function () {
return {
templateUrl: 'components/directives/fileButton/fileButton.html',
restrict: 'E',
link: function (scope, element, attributes) {
var container = angular.element('.file-upload-container');
var button = angular.element('.file-upload-button');
container.css({
position: 'relative',
overflow: 'hidden',
width: button.offsetWidth,
height: button.offsetHeight
})
}
};
});
a jade template if you are using jade
div(class="file-upload-container")
button(class="file-upload-button") +
input#file-upload(class="file-upload-input", type='file', onchange="doSomethingWhenFileIsSelected()")
the same template in html if you are using html
<div class="file-upload-container">
<button class="file-upload-button"></button>
<input class="file-upload-input" id="file-upload" type="file" onchange="doSomethingWhenFileIsSelected()" />
</div>
the css
.file-upload-button {
margin-top: 40px;
padding: 30px;
border: 1px solid black;
height: 100px;
width: 100px;
background: transparent;
font-size: 66px;
padding-top: 0px;
border-radius: 5px;
border: 2px solid rgb(255, 228, 0);
color: rgb(255, 228, 0);
}
.file-upload-input {
position: absolute;
top: 0;
left: 0;
z-index: 2;
width: 100%;
height: 100%;
opacity: 0;
cursor: pointer;
}
It's also easy to style the label if you are working with Bootstrap and LESS:
label {
.btn();
.btn-primary();
> input[type="file"] {
display: none;
}
}

Hide the browse button on a input type=file

Is there a way to hide the browse button and only leave the text box that works in all browsers?
I have tried setting the margins but they show up different in each browser
No, what you can do is a (ugly) workaround, but largely used
Create a normal input and a image
Create file input with opacity 0
When the user click on the image, you simulate a click on the file input
When file input change, you pass it's value to the normal input (so user can see the path)
Here you can see a full explanation, along with code:
http://www.quirksmode.org/dom/inputfile.html
You may just without making the element hidden, simply make it transparent by making its opacity to 0.
Making the input file hidden will make it STOP working. So DON'T DO THAT..
Here you can find an example for a transparent Browse operation;
.dropZoneOverlay, .FileUpload {
width: 283px;
height: 71px;
}
.dropZoneOverlay {
border: dotted 1px;
font-family: cursive;
color: #7066fb;
position: absolute;
top: 0px;
text-align: center;
}
.FileUpload {
opacity: 0;
position: relative;
z-index: 1;
}
<div class="dropZoneContainer">
<input type="file" id="drop_zone" class="FileUpload" accept=".jpg,.png,.gif" onchange="handleFileSelect(this) " />
<div class="dropZoneOverlay">Drag and drop your image <br />or<br />Click to add</div>
</div>
I find a good way of achieving this at Remove browse button from input=file.
The rationale behind this solution is that it creates a transparent input=file control and creates an layer visible to the user below the file control. The z-index of the input=file will be higher than the layer.
With this, it appears that the layer is the file control itself. But actually when you clicks on it, the input=file is the one clicked and the dialog for choosing file will appear.
Below code is very useful to hide default browse button and use custom instead:
(function($) {
$('input[type="file"]').bind('change', function() {
$("#img_text").html($('input[type="file"]').val());
});
})(jQuery)
.file-input-wrapper {
height: 30px;
margin: 2px;
overflow: hidden;
position: relative;
width: 118px;
background-color: #fff;
cursor: pointer;
}
.file-input-wrapper>input[type="file"] {
font-size: 40px;
position: absolute;
top: 0;
right: 0;
opacity: 0;
cursor: pointer;
}
.file-input-wrapper>.btn-file-input {
background-color: #494949;
border-radius: 4px;
color: #fff;
display: inline-block;
height: 34px;
margin: 0 0 0 -1px;
padding-left: 0;
width: 121px;
cursor: pointer;
}
.file-input-wrapper:hover>.btn-file-input {
//background-color: #494949;
}
#img_text {
float: right;
margin-right: -80px;
margin-top: -14px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<div class="file-input-wrapper">
<button class="btn-file-input">SELECT FILES</button>
<input type="file" name="image" id="image" value="" />
</div>
<span id="img_text"></span>
</body>
Came across this question and didn't feel like any of the answers were clean. Here is my solution:
<label>
<span>Select file</span>
<input type="file" style="display: none">
</label>
When you click the label the select file dialog will open. No js needed to make it happen.
You can style the label to look like a button.
Here is an example using w3css and font awesome:
<label class="w3-button w3-blue w3-round">
<span><i class="fas fa-image"></i></span>
<input type="file" style="display: none" >
</label>
Of course you need to add an event listener to the input to detect a file was chosen.
HTML - InputFile component can be hide by writing some css.
Here I am adding an icon which overrides inputfile component.
<label class="custom-file-upload">
<InputFile OnChange="HandleFileSelected" />
<i class="fa fa-cloud-upload"></i> Upload
</label>
css-
<style>
input[type="file"] {
display: none;
}
.custom-file-upload {
border: 1px solid #ccc;
display: inline-block;
padding: 6px 12px;
cursor: pointer;
}
</style>
So I found this solution that is very easy to implement and gives a very clean GUI
put this in your HTML
<label class="att-each"><input type="file"></label>
and this in your CSS
label.att-each {
width: 68px;
height: 68px;
background: url("add-file.png") no-repeat;
text-indent: -9999px;
}
add-file.png can be any graphic you wish to show on the webpage. Clicking the graphic will launch the default file explorer.
Working Example: http://www.projectnaija.com/file-picker17.html
Just an additional hint for avoiding too much JavaScript here: if you add a label and style it like the "browse button" you want to have, you could place it over the real browse button provided by the browser or hide the button somehow differently. By clicking the label the browser behavior is to open the dialog to browse for the file (don't forget to add the "for" attribute on the label with value of the id of the file input field to make this happen). That way you can customize the button in almost any way you want.
In some cases, it might be necessary to add a second input field or text element to display the value of the file input and hide the input completely as described in other answers. Still the label would avoid to simulate the click on the text input button by JavaScript.
BTW a similar hack can be used for customizing checkboxes or radiobuttons. by adding a label for them, clicking the label causes to select the checkbox/radiobutton. The native checkbox/radiobutton then can be hidden somewere and be replaced by a custom element.
Just add negative text intent as so:
input[type=file] {
text-indent: -120px;
}
before:
after:
Oddly enough, this works for me (when I place inside a button tag).
.button {
position: relative;
input[type=file] {
color: transparent;
background-color: transparent;
position: absolute;
left: 0;
width: 100%;
height: 100%;
top: 0;
opacity: 0;
z-index: 100;
}
}
Only tested in Chrome (macOS Sierra).
the best way for it
<input type="file" id="file">
<label for="file" class="file-trigger">Click Me</label>
And you can style your "label" element
#file {
display: none;
}
.file-trigger {
/* your style */
}
As of 2022, modern browsers support file button pseudo selector. I was only struggling with Safari v16.1 which didn't work as expected and had to workaround button hiding (::-webkit-file-upload-button part).
input[type=file]::file-selector-button {
display: none;
}
input[type=file]::-webkit-file-upload-button {
display: block;
width: 0;
height: 0;
margin-left: -100%;
}
input[type=file]::-ms-browse {
display: none;
}
You may also use concise syntax:
::file-selector-button {
/* ... */
}
::-webkit-file-upload-button {
/* ... */
}
::-ms-browse {
/* ... */
}