Using button HTML element for implementing custom checkbox - javascript

Is it viable to use <button> element instead of <input type="checkbox" />? Can it break accessibility or in any way affect the user experience? From what I noticed, we lose the element's value and onChange callback, but that's something easily fixed in React.
If anyone wonders, the reason for using would be much easier customization (styling).

To respect accessibility (a11y), if you wish to customize a checkbox, you can use anything, but you will have to map to your change event using an hidden but real <input type="checkbox" /> behind it.
Here is an example taken from the source below.
html {
box-sizing: border-box;
background: #f2f2f2;
padding: 20px 50px
}
/* Inherit box-sizing to make it easier to change the property
* for components that leverage other behavior.*/
*,
*::before,
*::after {
box-sizing: inherit;
}
/*style form to limit width and highlight the long label*/
form {
margin: 1rem auto;
max-width: 750px;
}
/*style wrapper to give some space*/
.wrapper {
position: relative;
margin-bottom: 1rem;
margin-top: 1rem;
}
/*style label to give some more space*/
.wrapper label {
display: block;
padding: 12px 0 12px 48px;
}
/*style and hide original checkbox*/
.wrapper input {
height: 40px;
left: 0;
opacity: 0;
position: absolute;
top: 0;
width: 40px;
}
/*position new box*/
.wrapper input + label::before {
border: 2px solid;
content: "";
height: 40px;
left: 0;
position: absolute;
top: 0;
width: 40px;
border-radius: 50%;
}
/*radio pseudo element styles*/
.wrapper input + label::after {
content: "";
opacity: 0;
border: 10px solid;
border-radius: 50%;
position: absolute;
left: 10px;
top: 10px;
transition: opacity 0.2s ease-in-out;
}
/*reveal check for 'on' state*/
.wrapper input:checked + label::after {
opacity: 1;
}
/*focus styles*/
.wrapper input:focus + label::before {
box-shadow: 0 0 0 3px #ffbf47;
outline: 3px solid transparent; /* For Windows high contrast mode. */
}
<form>
<fieldset>
<legend>
<h3>What type of accessibilty issues do you see most often?</h3>
</legend>
<div class="wrapper">
<input id="a11y-issue-1" name="a11y-issues" type="radio" value="no-issues">
<label for="a11y-issue-1">There are no issues</label>
</div>
<div class="wrapper">
<input id="a11y-issue-2" name="a11y-issues" type="radio" value="no-focus-styles">
<label for="a11y-issue-2">Focus styles are not present</label>
</div>
<div class="wrapper">
<input id="a11y-issue-3" name="a11y-issues" type="radio" value="html-markup">
<label for="a11y-issue-3">HTML markup is used in bizarre way. Also, what happens if the label text is very looooooooong, like this one?</label>
</div>
</fieldset>
</form>
Source: https://webdesign.tutsplus.com/tutorials/how-to-make-custom-accessible-checkboxes-and-radio-buttons--cms-32074

Related

Active Toggle switch to change pricing

I built a page that has three payment plans. There is also a Toggle switch, to change between Monthly and Annually. I have the price set to ex. $19.99 for monthly and when I click the toggle button to switch to annually I want the price to change to $199.99. Here is the code I have so far. All HTML and CSS so now my JS isn't working for it. What's wrong?
function myFunction() {
var x = document.getElementsByClassName("switch");
if (x.innerHTML === "$19.99") {
x.innerHTML = "$199.99";
} else {
x.innerHTML = "$19.99";
}
}
input.cmn-toggle-round:checked+label:after {
margin-left: 32px;
}
.switch {
position: relative;
left: 47.5%;
}
.cmn-toggle {
position: absolute;
margin-left: -9999px;
visibility: hidden;
}
.cmn-toggle+label {
display: block;
position: relative;
cursor: pointer;
outline: none;
user-select: none;
}
input.cmn-toggle-round+label {
padding: 2px;
width: 50px;
height: 20px;
background-color: #dddddd;
border-radius: 60px;
}
input.cmn-toggle-round+label:before,
input.cmn-toggle-round+label:after {
display: block;
position: absolute;
top: 1px;
left: 1px;
bottom: 1px;
content: "";
}
input.cmn-toggle-round+label:before {
right: 1px;
background-color: #f1f1f1;
border-radius: 60px;
transition: background 0.4s;
}
input.cmn-toggle-round+label:after {
width: 20px;
background-color: #fff;
border-radius: 100%;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
transition: margin 0.4s;
}
input.cmn-toggle-round:checked+label:before {
background-image: Linear-Gradient( to left, hsl(236, 72%, 79%), hsl(237, 63%, 64%)
);
}
<div class="switch" onclick="myFunction()">
<input id="cmn-toggle-1" class="cmn-toggle cmn-toggle-round" type="checkbox" />
<label for="cmn-toggle-1"></label>
</div>
There are several problem with your code. First you compare against the innerHTML property which isn't initially set this can not work until you have a default value.
The next is that you want to set the wrapper containers innerHTML which will remove the button.
In the solution below I introduced an paragraph with an initialValue of 199.95 and then toggle on it.
Also I replaced the incline scripting and used instead an eventListener.
let inp = document.getElementById("cmn-toggle-1");
inp.addEventListener("click", myFunction);
function myFunction() {
var x = document.getElementById("par");
if(x.innerHTML === "$199.99"){
x.innerHTML = "$19.99";
}else{
x.innerHTML = "$199.99";
}
}
input.cmn-toggle-round:checked+label:after {
margin-left: 32px;
}
.switch {
position: relative;
left: 47.5%;
}
.cmn-toggle {
position: absolute;
margin-left: -9999px;
visibility: hidden;
}
.cmn-toggle+label {
display: block;
position: relative;
cursor: pointer;
outline: none;
user-select: none;
}
input.cmn-toggle-round+label {
padding: 2px;
width: 50px;
height: 20px;
background-color: #dddddd;
border-radius: 60px;
}
input.cmn-toggle-round+label:before,
input.cmn-toggle-round+label:after {
display: block;
position: absolute;
top: 1px;
left: 1px;
bottom: 1px;
content: "";
}
input.cmn-toggle-round+label:before {
right: 1px;
background-color: #f1f1f1;
border-radius: 60px;
transition: background 0.4s;
}
input.cmn-toggle-round+label:after {
width: 20px;
background-color: #fff;
border-radius: 100%;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
transition: margin 0.4s;
}
input.cmn-toggle-round:checked+label:before {
background-image: Linear-Gradient( to left, hsl(236, 72%, 79%), hsl(237, 63%, 64%));
}
<div class="switch">
<input id="cmn-toggle-1" class="cmn-toggle cmn-toggle-round" type="checkbox" />
<label for="cmn-toggle-1"></label>
<p id="par">$199.99</p>
</div>
Having just read the comment about using the code to manipulate prices for several elements you need a different approach as using an ID will no longer work - ID attributes must be unique so unless you get tricky with numerically incremented indices and the like ( messy ) you can inspect the target attribute of any click event ( or other type ) and use parent/child/sibling selectors to identify and manipulate nodes of interest.
The HTML below was slightly modified by adding dataset attributes and ignoring any ID attributes present. The CSS was hastily fudged to facilitate the display.... but I think it shows how you might do it.
An event listener is assigned to each parent DIV (.switch) and the currentTarget property of the event is used to find the label which actually receives the click. Using the combination of parentNode and querySelector we can identify the nodes we need to make the logic work
document.querySelectorAll('div.switch').forEach( div=>{
div.addEventListener('click',e=>{
if( e.target === e.target.parentNode.lastElementChild ) {
let span=e.target.parentNode.querySelector('span');
document.documentElement.style.setProperty('--currency','$')
span.dataset.price=e.currentTarget.dataset.state == 1 ? e.currentTarget.dataset.annual : e.currentTarget.dataset.month;
span.dataset.period=e.currentTarget.dataset.state == 1 ? 'Monthly:' : 'Annually:';
span.dataset.currency=document.documentElement.style.getPropertyValue('--currency');
e.currentTarget.dataset.state = 1 - Number( e.currentTarget.dataset.state );
}
})
})
input.cmn-toggle-round:checked + label:after {
margin-left: 32px;
}
.switch {
--left:47.5%;
position: relative;
left: var(--left);
margin:1rem 0 1.5rem 0;
width:calc(80% - var(--left));
}
.cmn-toggle {
position: absolute;
margin-left: -9999px;
visibility: hidden;
}
.cmn-toggle+label {
display: block;
position: relative;
cursor: pointer;
outline: none;
user-select: none;
}
input.cmn-toggle-round+label {
padding: 2px;
width: 50px;
height: 20px;
background-color: #dddddd;
border-radius: 60px;
}
input.cmn-toggle-round+label:before,
input.cmn-toggle-round+label:after {
display: block;
position: absolute;
top: 1px;
left: 1px;
bottom: 1px;
content: "";
}
input.cmn-toggle-round+label:before {
right: 1px;
background-color: #f1f1f1;
border-radius: 60px;
transition: background 0.4s;
}
input.cmn-toggle-round + label:after {
width: 20px;
background-color: #fff;
border-radius: 100%;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
transition: margin 0.4s;
}
input.cmn-toggle-round:checked + label:before {
background-image: Linear-Gradient( to left, hsl(236, 72%, 79%), hsl(237, 63%, 64%) );
}
span:after{
display:inline-block;
float:right;
clear:none;
margin:0;
content:attr(data-period)' 'attr(data-currency)attr(data-price)!important;
}
<div class='switch' data-state=0 data-month='199.99' data-annual='19.99'>
<span></span>
<input id='cmn-toggle-1' class='cmn-toggle cmn-toggle-round' type='checkbox' />
<label for='cmn-toggle-1'></label>
</div>
<div class='switch' data-state=0 data-month='259.99' data-annual='23.99'>
<span></span>
<input id='cmn-toggle-2' class='cmn-toggle cmn-toggle-round' type='checkbox' />
<label for='cmn-toggle-2'></label>
</div>
<div class='switch' data-state=0 data-month='150.00' data-annual='75.00'>
<span></span>
<input id='cmn-toggle-3' class='cmn-toggle cmn-toggle-round' type='checkbox' />
<label for='cmn-toggle-3'></label>
</div>
<div class='switch' data-state=0 data-month='652.50' data-annual='480.50'>
<span></span>
<input id='cmn-toggle-4' class='cmn-toggle cmn-toggle-round' type='checkbox' />
<label for='cmn-toggle-4'></label>
</div>
<div class='switch' data-state=0 data-month='40.00' data-annual='19.99'>
<span></span>
<input id='cmn-toggle-5' class='cmn-toggle cmn-toggle-round' type='checkbox' />
<label for='cmn-toggle-5'></label>
</div>
<div class='switch' data-state=0 data-month='300.00' data-annual='14.99'>
<span></span>
<input id='cmn-toggle-6' class='cmn-toggle cmn-toggle-round' type='checkbox' />
<label for='cmn-toggle-6'></label>
</div>

How can I fix the textarea border bottom css effect?

I have a simple border bottom animation with another element and it works fine on simple input element but it's not work properly on a textarea. (If I should use javaScript then please advise a solution)
How can I fix the height of textarea?
*{
box-sizing: border-box;
}
.container {
position: relative;
}
textarea,
input {
border: none;
border-bottom: 3px solid #e6e6e6;
width: 100%;
height: 50px;
resize: none;
}
.anim-bar {
position: absolute;
bottom: 0;
left: 0;
height: 3px;
width: 0%;
background-color: blue;
-webkit-transition: width .3s;
transition: width .3s;
}
textarea:focus + .anim-bar,
input:focus + .anim-bar {
width: 100%;
}
<h1>Works fine on input :)</h1>
<div class="container">
<input type="text">
<span class="anim-bar"></span>
</div>
<h1>Cross the container box- textarea :( </h1>
<div class="container">
<textarea></textarea>
<span class="anim-bar"></span>
</div>
this issue not show on all browsers. in my browsers chrome(window) is obvious.
the line is under textarea.
======================================================
I think is baseline is different.
Baseline inconsistency
The HTML specification doesn't define where the baseline of a is, so different browsers set it to different positions. For Gecko, the baseline is set on the baseline of the first line of the textarea's first line, on another browser it may be set on the bottom of the box. Don't use vertical-align: baseline on it; the behavior is unpredictable.
reference:
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea
* {
box-sizing: border-box;
}
.container {
position: relative;
}
textarea,
input {
border: none;
border-bottom: 3px solid #e6e6e6;
width: 100%;
height: 50px;
resize: vertical;
/* see this */
vertical-align: middle;
}
.anim-bar {
position: absolute;
bottom: 0;
left: 0;
height: 3px;
width: 0%;
background-color: blue;
-webkit-transition: width .3s;
transition: width .3s;
}
textarea:focus+.anim-bar,
input:focus+.anim-bar {
width: 100%;
}
<h1>Works fine on input :)</h1>
<div class="container">
<input type="text">
<span class="anim-bar"></span>
</div>
<h1>Error on textarea :( </h1>
<div class="container">
<textarea></textarea>
<span class="anim-bar"></span>
</div>
==================================================
Adding CSS styling to the textarea resize: none; will stop the textarea from being adjustable.
I added the styling to the CSS area and inline the HTML to show both ways it can be done.
* {
box-sizing: border-box;
}
.container {
position: relative;
}
textarea {
resize:none; //adding styling in css file
}
textarea,
input {
border: none;
border-bottom: 3px solid #e6e6e6;
width: 100%;
height: 50px;
resize: vertical;
}
.anim-bar {
position: absolute;
bottom: 0;
left: 0;
height: 3px;
width: 0%;
background-color: blue;
-webkit-transition: width .3s;
transition: width .3s;
}
textarea:focus+.anim-bar,
input:focus+.anim-bar {
width: 100%;
}
<h1>Works fine on input :)</h1>
<div class="container">
<input type="text">
<span class="anim-bar"></span>
</div>
<h1>Error on textarea :( </h1>
<div class="container">
<textarea style="resize:none;"></textarea> <!--Adding CSS styling inline-->
<span class="anim-bar"></span>
</div>

How does bootstrap determine checked state of checkbox/radiobutton

I'm currently trying to debug an application that consumes bootstrap HTML and CSS and converts it to a different document format. When using the 'custom-control-input' class for styling on the checkboxes, the checkboxes are being displayed but the 'checked' attribute is lost and I end up with an unchecked bootstrap checkbox.
I'm certain the application checks to see if the attribute is set, and normal checkboxes get parsed correctly.
I know that when bootstrap is used to create checkboxes in the following way:
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="defaultUnchecked">
<label class="custom-control-label" for="defaultUnchecked">Default unchecked</label>
bootstrap uses opacity to hide the checkbox and then uses the 'custom control label' to display the bootstrap checkbox.
What I cannot seem to figure out is how bootstrap then determines if the checkbox is supposed to be checked. Does it use only CSS rules for this, or is there some JavaScript/JQuery involved?
I checked how bootstrap works in this case, this is done with the help of a :checked pseudo-class. Link https://developer.mozilla.org/ru/docs/Web/CSS/:checked.
Here is the relatively all code that is a bootstrap implementation.
.custom-control {
position: relative;
display: block;
min-height: 1.5rem;
padding-left: 1.5rem;
}
.custom-control-input {
position: absolute;
left: 0;
z-index: -1;
width: 1rem;
height: 1.25rem;
opacity: 0;
}
.custom-control-label {
position: relative;
margin-bottom: 0;
vertical-align: top;
}
label {
display: inline-block;
line-height: 1.5;
}
.custom-control-input:checked~.custom-control-label::before {
color: #fff;
border-color: #007bff;
background-color: #007bff;
}
.custom-checkbox .custom-control-label::before {
border-radius: .25rem;
}
.custom-control-label::before, .custom-file-label, .custom-select {
transition: background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
}
.custom-control-label::before {
position: absolute;
top: .25rem;
left: -1.5rem;
display: block;
width: 1rem;
height: 1rem;
pointer-events: none;
content: "";
background-color: #fff;
border: #adb5bd solid 1px;
}
.custom-checkbox .custom-control-input:checked~.custom-control-label::after {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e");
}
.custom-control-label::after {
position: absolute;
top: .25rem;
left: -1.5rem;
display: block;
width: 1rem;
height: 1rem;
content: "";
background: no-repeat 50%/50% 50%;
}
*, ::after, ::before {
box-sizing: border-box;
}
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="exampleCheck1">
<label class="custom-control-label" for="exampleCheck1">Check me out</label>
</div>

Masking labels for Checkboxes - The correct way?

I am aware that we can create customised checkboxes using <label for="#checkbox_id"> using the for attribute and referencing the input's id.
I need to know which would be the best way to hide the background checkbox tag i.e, <input type="checkbox" class="masked-input" id="checkbox_id"/>
I'm currently using .masked-input{display: none;} .No issues for me so far.
But I have seen in many webpages, they don't use the display:none property.
Instead they use
.checkbox{
height: 0;
width: 0;
border: 0;
overflow: hidden;
visibility: hidden;
}
Why wouldn't they just use display:none; and keep this simple? Or am I missing something or doing it the wrong way?
User Accessibility Issue:
Its a very straight forward question. You should use
.checkbox{
height: 0;
width: 0;
border: 0;
overflow: hidden;
visibility: hidden;
}
or
.checkbox{
opacity: 0;
}
because if you use display: none; it will be hard to access by the user. For instance if the user is trying to access the checkbox by pressing the tab key, the checkbox with the display none property will get skipped from the focus.
In our example below, the second checkbox property is set to display: none; and other two checkboxes are set with opacity: 0; you can see the focus by pressing tab key and how the second checkbox is getting skipped.
.styled-checkbox {
position: absolute;
opacity: 0;
}
#styled-checkbox-2{
display: none;
}
.styled-checkbox + label {
position: relative;
cursor: pointer;
padding: 0;
}
.styled-checkbox + label:before {
content: '';
margin-right: 10px;
display: inline-block;
vertical-align: text-top;
width: 20px;
height: 20px;
background: white;
}
.styled-checkbox:hover + label:before {
background: #f35429;
}
.styled-checkbox:focus + label:before {
box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.12);
}
.styled-checkbox:checked + label:before {
background: #f35429;
}
.styled-checkbox:disabled + label {
color: #b8b8b8;
cursor: auto;
}
.styled-checkbox:disabled + label:before {
box-shadow: none;
background: #ddd;
}
.styled-checkbox:checked + label:after {
content: '';
position: absolute;
left: 5px;
top: 9px;
background: white;
width: 2px;
height: 2px;
box-shadow: 2px 0 0 white, 4px 0 0 white, 4px -2px 0 white, 4px -4px 0 white, 4px -6px 0 white, 4px -8px 0 white;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
html {
background: lightgray;
}
body {
font-family: 'Source Sans Pro', sans-serif;
}
.unstyled {
margin: 0;
padding: 0;
list-style-type: none;
}
li {
margin: 20px 0;
}
.centered {
width: 300px;
margin: auto;
}
.title {
text-align: center;
color: #4571ec;
}
<h1 class="title">Pure CSS Custom Checkboxes</h1>
<ul class="unstyled centered">
<li>
<input class="styled-checkbox" id="styled-checkbox-1" type="checkbox" value="value1">
<label for="styled-checkbox-1">Checkbox</label>
</li>
<li>
<input class="styled-checkbox" id="styled-checkbox-2" type="checkbox" value="value2">
<label for="styled-checkbox-2">CSS Only</label>
</li>
<li>
<input class="styled-checkbox" id="styled-checkbox-3" type="checkbox" value="value4">
<label for="styled-checkbox-3">Fourth option</label>
</li>
</ul>
The code you use is often used when there is a custom styled checkbox. They hide the original checkbox and display a custom one instead. In order to still keep the functionality they only hide the checkbox.
If you use display: none the complete checkbox will be removed from the DOM, making it impossible to click.
As https://kyusuf.com/post/completely-css-custom-checkbox-radio-buttons-and-select-boxes states:
Note that we are hiding the input with z-index: -1; and opacity: 0; -
using display: none; or visibility: hidden; would stop the inputs
functioning correctly. Onto the .control__indicator - this is what we
will style to look like checkboxes/radio buttons.

Jquery click event work only when page to top position

I have CSS tabs:
.tabs {
width: 100%;
height: 100%;
margin: 0 0 30px;
}
.tabs label {
float: left;
display: inline;
margin: 0 1px -1px 0;
padding: 0 13px 1px;
color: #777;
cursor: pointer;
background: #F9F9F9;
border: 1px solid #E4E4E4;
border-bottom: 1px solid #F9F9F9;
position: relative;
line-height: 25px;
z-index: 1;
}
.tabs label:hover {
color: #F70;
padding: 0 13px;
background: #FFFFDF;
border: 1px solid #FFCA95;
}
.tabs input {
position: absolute;
left: -9999px;
}
#tab_1:checked ~ #tab_l1,
#tab_2:checked ~ #tab_l2 {
color: #444;
background: #EFEFEF;
padding: 0 13px 2px;
border: 1px solid #D4D4D4;
border-bottom: 1px solid #EFEFEF;
z-index: 3
}
.tabs_cont {
position: relative;
height: 552px;
border: 1px solid #DDD;
border-width: 1px;
background: #EFEFEF;
padding: 0 12px;
z-index: 2;
}
.tabs_cont > div {
position: absolute;
left: -9999px;
top: 0;
opacity: 0;
-moz-transition: opacity .2s ease-in-out;
-webkit-transition: opacity .2s ease-in-out;
transition: opacity .2s ease-in-out;
}
#tab_1:checked ~ .tabs_cont #tab_c1,
#tab_2:checked ~ .tabs_cont #tab_c2 {
position: static;
left: 0;
opacity: 1;
}
and html:
<section class="tabs">
<input id="tab_1" type="radio" name="tab" checked="checked" />
<input id="tab_2" type="radio" name="tab" />
<label for="tab_1" id="tab_l1">Изображения</label>
<label for="tab_2" id="tab_l2">Текст</label>
<div style="clear:both"></div>
<div class="tabs_cont">
<div id="tab_c1"> </div>
<div id="tab_c2">
<div class="add_element" id="add_text">добавить текст </div>
<div id="text_inputs_wrapper"> </div>
</div>
</div>
</section>
And JS:
$("div#add_text").click(function () //on add input button click
{
alert( "Handler for .click() called." );
});
When the page is in the upper position, the event is running. If I`m use scroll and page move - event is not running.
The problem occurs in all browsers.
Has anyone encountered this problem? Help please.
UPDATE.
Please see picture
in this case the event is running
http://1drv.ms/1wg73ak
in this case the event is not running
http://1drv.ms/1mt24JS
As Vector said, you can only click on the #add_text div to trigger the event. The #add_text div is only has a height of one line so you have to click right on the text.
Add a height: 100% to your tab_c2 and to the #add_text then you can click anywhere on the 2nd tab page to trigger the event.
Append this code to your css, your div#add_text will fill the entire container area and the click is works in all position
#add_text {
background-color: red; //remove this later
position: absolute;
width: 100%;
height: 100%;
left: 0px;
top: 0px;
}

Categories