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>
I need to make that switch slider move to the word user has clicked on (medium or large). I don't know if I can track movement of switch slider with JavaScript or maybe it is possible to do with css. For now, I have only created simple toggle switcher with changing color. The problem is that slider was customised a lot with :before, :checked, input and etc so I don't realise even where to start to make it happen.
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
.row-inputs-radio {
display: flex;
justify-content: space-between;
}
.item-size {
margin-left: 10px;
}
.item-size-eg {
font-size: 15px;
color: rgb(155, 154, 154);
}
.continue-btn button {
outline: none;
}
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 10px;
margin-top: 15px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: -74px;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
width: 187px;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: -3px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
border: 1px solid grey;
}
input:checked + .slider {
background-color: #13985C;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider:before {
-webkit-transform: translateX(120px);
-ms-transform: translateX(120px);
transform: translateX(162px);
}
<div class="column-input">
<div class="row-inputs row-inputs-radio">
<div class="row-input">
<div class="item-size"><bold>Medium</bold></div>
</div>
<div class="row-input">
<label class="switch">
<input type="checkbox" checked />
<span class="slider round"></span>
</label>
</div>
<div class="row-input">
<div class="item-size"><bold>Large</bold></div>
</div>
</div>
Let's go step by step:
We get the medium, large, and input elements
Add onclick eventListeners to them for toggling
Add cursor: pointer to show they're clickable
var medium = document.querySelector('.medium'),
large = document.querySelector('.large'),
slider = document.querySelector('.switch input');;
medium.onclick = function() {
slider.checked = false;
}
large.onclick = function() {
slider.checked = true;
}
.medium, .large {
cursor: pointer;
}
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
.row-inputs-radio {
display: flex;
justify-content: space-between;
}
.item-size {
margin-left: 10px;
}
.item-size-eg {
font-size: 15px;
color: rgb(155, 154, 154);
}
.continue-btn button {
outline: none;
}
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 10px;
margin-top: 15px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: -74px;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
width: 187px;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: -3px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
border: 1px solid grey;
}
input:checked+.slider {
background-color: #13985C;
}
input:focus+.slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked+.slider:before {
-webkit-transform: translateX(120px);
-ms-transform: translateX(120px);
transform: translateX(162px);
}
<div class="column-input">
<div class="row-inputs row-inputs-radio">
<div class="row-input">
<div class="item-size medium">
<bold>Medium</bold>
</div>
</div>
<div class="row-input">
<label class="switch">
<input type="checkbox" checked />
<span class="slider round"></span>
</label>
</div>
<div class="row-input">
<div class="item-size large">
<bold>Large</bold>
</div>
</div>
</div>
Using addEventListener()
The wanted behavior can be achieved by adding an event-listener to the labels to (un-)check the checkbox.
We can use Event delegation to only add one event-listener to the parent. We can use a boolean that tells us if the clicked label is the "on"-label (meaning, the label that should check the checkbox).
This boolean can immediately be used as the value the .checked-attribute should now have. Quick explanation:
Is the label the "on"-label? If yes, do check
Is the label the "on"-label? If no, do not check
In pseudo-code, it would look like this:
checkbox.checked = isOnLabel
We can check if the clicked label is the "on"-label by looking if the .row-input-div it is inside of is the last one.
Note:
Only adding a click-listener to an element which isn't clickable by default doesn't make it accessible.
To make an element accessible clickable, one would have to:
Add all the appropriate listeners (click-listener, key-listener; specific elements listen for specific keys, e.g. a button for 'space', an anchor for 'enter', a radio-group for the arrow-keys)
Make the element ARIA-conform (use semantically meaningful HTML, e.g. specific HTML-elements, aria-role, aria-label)
This can be quite a lot of work, so unless there is a good reason to create an element from new, one should stick to what is currently given in the HTML-specification (though it is easier to read up on HTML-elements on MDN).
Here is the snippet to show the result:
Note: The code also works for multiple .column-inputs.
for (let ci of document.querySelectorAll('.column-input')) {
ci.addEventListener('click', evt => {
let ri = evt.target.closest('.row-input'); // Get the '.row-input'-div
if (ri.querySelector('.item-size')) { // If `ri` contains '.item-size' / a label, continue
// '!ri.nextElementSibling' is true if `ri` is the last element
ci.querySelector('input').checked = !ri.nextElementSibling;
}
});
}
.slider.round {border-radius: 34px}
.slider.round:before {border-radius: 50%}
.row-inputs-radio {
display: flex;
justify-content: space-between;
}
.item-size {margin-left: 10px}
.item-size-eg {
font-size: 15px;
color: rgb(155, 154, 154);
}
.continue-btn button {outline: none}
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 10px;
margin-top: 15px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: -74px;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
width: 187px;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: -3px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
border: 1px solid grey;
}
input:focus + .slider {box-shadow: 0 0 1px #2196F3}
input:checked + .slider {background-color: #13985C}
input:checked + .slider:before {
-webkit-transform: translateX(120px);
-ms-transform: translateX(120px);
transform: translateX(162px);
}
<div class="column-input">
<div class="row-inputs row-inputs-radio">
<div class="row-input">
<div class="item-size"><bold>Medium</bold></div>
</div>
<div class="row-input">
<label class="switch">
<input type="checkbox" checked />
<span class="slider round"></span>
</label>
</div>
<div class="row-input">
<div class="item-size"><bold>Large</bold></div>
</div>
</div>
</div>
<div class="column-input">
<div class="row-inputs row-inputs-radio">
<div class="row-input">
<div class="item-size"><bold>Mayo</bold></div>
</div>
<div class="row-input">
<label class="switch">
<input type="checkbox" checked />
<span class="slider round"></span>
</label>
</div>
<div class="row-input">
<div class="item-size"><bold>Ketchup</bold></div>
</div>
</div>
</div>
HTML/CSS only
One could also make a slider-like element using radio-groups, where the radio-group would consist of two elements: "on" and "off".
In combination with labels, the user (sighted or tool-assisted) knows what both options of the sliders are, and can select one accordingly, either by using the mouse or by using the keyboard.
You can try it yourself! Tab to the radio-group, and press the arrow-keys.
Associating a label with an input-element has the following advantages:
It correctly labels its input-element for tools (e.g. screen-readers)
Clicking a label behaves the same way as clicking its input-element itself
(the effect we achieved before using addEventListener() is implemented for associated labels by default)
It doesn't require any JavaScript
For graphics, instead of misusing an HTML-element, I used SVG in combination with CSS.
Having multiple input-elements has the advantage of being able to give each one an individual value. This might be useful for submitting a <form>, so that the back-end would have an easier time figuring out what the values mean (e.g. a radio-groups' "Mayo" / "Ketchup" versus a checkbox' "Mayo or Ketchup" = (true | false)). This also makes maintenance easier, and the back-end wouldn't have to look into front-end code to understand the meaning of a value.
Notice the aria-hidden="true"-attribute for the <label>s inside the <svg>. That tells the browser to ignore those tags for building the Accessibility Tree, meaning only the labels with the labelling content will be associated with their input-elements.
As far as I know, this should be one way to make the slider-mechanic you want fully accessible, for both mouse- and keyboard-users, as well as sighted and tool-assisted users.
/* OPTIONAL
*
* HTML-element can be used without JS.
* However, one would need to manually add
* the radio-names in these places:
* - as <input>'s names
* - prepend <input>'s id
* - prepend <label>'s for
*/
for (var rs of document.querySelectorAll('.radio-slider')) {
var rsName = rs.getAttribute('data-name') || 'broken';
for (var rsInput of rs.querySelectorAll('input')) {
rsInput.id = rsName + rsInput.id;
rsInput.name = rsName;
}
for (var rsLabel of rs.querySelectorAll('label')) {
rsLabel.setAttribute('for', rsName + rsLabel.getAttribute('for'));
}
}
/* Tool-classes (TC); DO NOT CHANGE */
/* TC: '.sr-only' */
.sr-only {
position: absolute;
margin: -1px;
padding: 0;
clip: rect(0,0,0,0);
width: 1px;
height: 1px;
border: 0;
overflow: hidden;
}
/* TC: '.radio-slider' */
.radio-slider svg { /* Dimension of 8:5 */
width: 2rem;
height: 1.25rem;
}
.radio-slider > div {display: flex}
.radio-slider input:focus-visible ~ div {outline: auto}
.radio-slider input:focus-visible ~ div svg rect {fill: #2196F3}
.radio-slider circle,
.radio-slider rect {transition: 0.5s}
.radio-slider foreignObject > label {
width: 100%;
height: 18px;
display: block;
}
.radio-slider .on:checked ~ div svg foreignObject:last-child {display: none}
.radio-slider .on:checked ~ div svg rect {fill: #13985C}
.radio-slider .on:checked ~ div svg circle {transform: translateX(calc(2rem - 16px))}
<div class="radio-slider" data-name="radio1">
<input id="-off" class="sr-only off" type="radio">
<input id="-on" class="sr-only on" type="radio">
<div>
<label for="-off">Medium</label>
<svg viewBox="0 0 32 20" xmlns="xmlns=http://www.w3.org/1999/xhtml" stroke="black" fill="lightgray" stroke-width="0.5">
<rect x="4" y="4" rx="4" width="24" height="8" />
<circle cx="8" cy="8" r="6" fill="white" />
<foreignObject width="100%" height="100%"><label for="-off" aria-hidden="true"></label></foreignObject>
<foreignObject width="100%" height="100%"><label for="-on" aria-hidden="true"></label></foreignObject>
</svg>
<label for="-on">Large</label>
</div>
</div>
<div class="radio-slider" data-name="radio2">
<input id="-off" class="sr-only off" type="radio">
<input id="-on" class="sr-only on" type="radio">
<div>
<label for="-off">Mayo</label>
<svg viewBox="0 0 32 20" xmlns="xmlns=http://www.w3.org/1999/xhtml" stroke="black" fill="lightgray" stroke-width="0.5">
<rect x="4" y="4" rx="4" width="24" height="8" />
<circle cx="8" cy="8" r="6" fill="white" />
<foreignObject width="100%" height="100%"><label for="-off"></label></foreignObject>
<foreignObject width="100%" height="100%"><label for="-on"></label></foreignObject>
</svg>
<label for="-on">Ketchup</label>
</div>
</div>
In my example, I did not assign a unique class to each side. And so I used the forEach() method.
let select = document.querySelectorAll('.row-input .item-size');
let checked_pos = document.querySelector('.row-input .switch input[type="checkbox"]');
Array.from(select).forEach(function(selectCurrent, index) {
selectCurrent.onclick = function() {
if (index == 0) {
checked_pos.checked = false;
}
if (index == 1) {
checked_pos.checked = true;
}
}
});
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
.row-inputs-radio {
display: flex;
justify-content: space-between;
}
.item-size {
margin-left: 10px;
}
.item-size-eg {
font-size: 15px;
color: rgb(155, 154, 154);
}
.continue-btn button {
outline: none;
}
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 10px;
margin-top: 15px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: -74px;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
width: 187px;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: -3px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
border: 1px solid grey;
}
input:checked + .slider {
background-color: #13985C;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider:before {
-webkit-transform: translateX(120px);
-ms-transform: translateX(120px);
transform: translateX(162px);
}
<div class="column-input">
<div class="row-inputs row-inputs-radio">
<div class="row-input">
<div class="item-size"><bold>Medium</bold></div>
</div>
<div class="row-input">
<label class="switch">
<input type="checkbox" checked/>
<span class="slider round"></span>
</label>
</div>
<div class="row-input">
<div class="item-size"><bold>Large</bold></div>
</div>
</div>
I would like to have an alternative to a standard checkbox - basically I'd like to use images and when the user clicks the image, fade it out and overlay a tick box.
In essence, I want to do something like Recaptcha 2 does when it gets you to click images that meet a certain criteria. You can see a Recaptcha demo here but it might sometimes get you to solve text questions, as opposed to the image selection. So here's a screenshot:
When you click one of the images (in this case, containing a picture of steak), the image you click shrinks in size and the blue tick appears, indicating that you've ticked it.
Let's say I want to reproduce this exact example.
I realise I can have 9 hidden checkboxes, and attach some jQuery so that when I click the image, it selects/deselects the hidden checkbox. But what about the shrinking of the image/overlaying the tick?
Pure semantic HTML/CSS solution
This is easy to implement on your own, no pre-made solution necessary. Also it will teach you a lot as you don't seem too easy with CSS.
This is what you need to do:
Your checkboxes need to have distinct id attributes. This allows you to connect a <label> to it, using the label's for-attribute.
Example:
<input type="checkbox" id="myCheckbox1" />
<label for="myCheckbox1"><img src="http://someurl" /></label>
Attaching the label to the checkbox will trigger a browser behaviour: Whenever someone clicks the label (or the image inside it), the checkbox will be toggled.
Next, you hide the checkbox by applying for example display: none; to it.
Now all that is left to do is set the style you want for your label::before pseudo element (which will be used as the visual checkbox replacement elements):
label::before {
background-image: url(../path/to/unchecked.png);
}
In a last tricky step, you make use of CSS' :checked pseudo selector to change the image when the checkbox is checked:
:checked + label::before {
background-image: url(../path/to/checked.png);
}
The + (adjacent sibling selector) makes sure you only change labels that directly follow the hidden checkbox in the markup.
You can optimize that by putting both images in a spritemap and only applying a change in background-position instead of swapping the image.
Of course you need to position the label correctly and apply display: block; and set correct width and height.
Edit:
The codepen example and snippet, which I created after these instructions, use the same technique, but instead of using images for the checkboxes, the checkbox replacements are done purely with CSS, creating a ::before on the label that, once checked, has content: "✓";. Add some rounded borders and sweet transitions and the result is really likable!
Here is a working codepen that showcases the technique and doesn't require images for the checkbox:
http://codepen.io/anon/pen/wadwpx
Here is the same code in a snippet:
ul {
list-style-type: none;
}
li {
display: inline-block;
}
input[type="checkbox"][id^="cb"] {
display: none;
}
label {
border: 1px solid #fff;
padding: 10px;
display: block;
position: relative;
margin: 10px;
cursor: pointer;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
label::before {
background-color: white;
color: white;
content: " ";
display: block;
border-radius: 50%;
border: 1px solid grey;
position: absolute;
top: -5px;
left: -5px;
width: 25px;
height: 25px;
text-align: center;
line-height: 28px;
transition-duration: 0.4s;
transform: scale(0);
}
label img {
height: 100px;
width: 100px;
transition-duration: 0.2s;
transform-origin: 50% 50%;
}
:checked+label {
border-color: #ddd;
}
:checked+label::before {
content: "✓";
background-color: grey;
transform: scale(1);
}
:checked+label img {
transform: scale(0.9);
box-shadow: 0 0 5px #333;
z-index: -1;
}
<ul>
<li><input type="checkbox" id="cb1" />
<label for="cb1"><img src="https://picsum.photos/seed/1/100" /></label>
</li>
<li><input type="checkbox" id="cb2" />
<label for="cb2"><img src="https://picsum.photos/seed/2/100" /></label>
</li>
<li><input type="checkbox" id="cb3" />
<label for="cb3"><img src="https://picsum.photos/seed/3/100" /></label>
</li>
<li><input type="checkbox" id="cb4" />
<label for="cb4"><img src="https://picsum.photos/seed/4/100" /></label>
</li>
</ul>
Pure CSS Solution
There are three neat devices invoked:
The :checked selector
The ::before pseudo-selector
The css content property.
label:before {
content: url("https://cdn1.iconfinder.com/data/icons/windows8_icons_iconpharm/26/unchecked_checkbox.png");
position: absolute;
z-index: 100;
}
:checked+label:before {
content: url("https://cdn1.iconfinder.com/data/icons/windows8_icons_iconpharm/26/checked_checkbox.png");
}
input[type=checkbox] {
display: none;
}
/*pure cosmetics:*/
img {
width: 150px;
height: 150px;
}
label {
margin: 10px;
}
<input type="checkbox" id="myCheckbox1" />
<label for="myCheckbox1">
<img src="https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcR0LkgDZRDTgnDrzhnXGDFRSItAzGCBEWEnkLMdnA_zkIH5Zg6oag">
</label>
<input type="checkbox" id="myCheckbox2" />
<label for="myCheckbox2">
<img src="https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcRhJjGB3mQxjhI5lfS9SwXou06-2qT_0MjNAr0atu75trXIaR2d">
</label>
<input type="checkbox" id="myCheckbox3" />
<label for="myCheckbox3">
<img src="https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcQuwWbUXC-lgzQHp-j1iw56PIgl_2eALrEENUP-ld72gq3s8cVo">
</label>
See this jQuery plugin: imgCheckbox (on npm and bower)
Disclaimer: No javascript is necessary to solve this problem. The tension is between maintainability and efficiency of code. While there's no need for a plugin (or any javascript), it sure does make it faster to build and often easier to change.
Barebones Solution:
With very simple HTML (none of the mess with checkboxes and labels etc.):
<img class="checkable" src="http://lorempixel.com/100/100" />
You can use jQuery's toggleClass to turn on/off a selected or checked class on the click event:
$("img.checkable").click(function () {
$(this).toggleClass("checked");
});
Checked items are fetched with
$(".checked")
Plus Coolness:
You can style the images based off of this but a big problem is that without other DOM elements you can't even use ::before and ::after to add stuff like check marks. The solution is to wrap your images with another element (and it makes sense to attach the click listener to the wrapped element as well).
$("img.checkable").wrap("<span class='fancychecks'>")
This leaves your html really clean and your js incredibly readable. Take a look at the snippet...
/* Note that this js actually responds
to a click event on the wrapped element!
(not the image) */
$("img.checkable").wrap("<span class='fancychecks'>")
.parent().click(function() {
$(this).toggleClass("checked");
});
/* style the images */
span.fancychecks img {
display: block;
margin: 0;
padding: 0;
transition-duration: 300ms;
transform: scale(1);
filter: none;
-webkit-filter: grayscale(0);
}
span.fancychecks.checked img {
transform: scale(0.8);
filter: gray;
filter: grayscale(1);
-webkit-filter: grayscale(1);
}
/* style the parent spans */
span.fancychecks {
padding: 0;
margin: 5px;
display: inline-block;
border: 1px solid transparent;
transition-duration: 300ms;
}
span.fancychecks.checked {
border-color: #ccc;
}
/* Using conexo's fantastic CSS, make the checkmarks */
span.fancychecks::before {
background-color: rgba(50, 200, 50, 0.7);
color: white;
content: "✓";
font-weight: bold;
border-radius: 50%;
position: absolute;
margin: 2px;
top: 1;
left: 1;
z-index: 1;
width: 25px;
height: 25px;
text-align: center;
line-height: 28px;
transform: scale(0);
transition-duration: 300ms;
}
span.fancychecks.checked::before {
transform: scale(1);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<img class="checkable" src="http://lorempixel.com/100/100/city/1" />
<img class="checkable" src="http://lorempixel.com/100/100/city/2" />
<img class="checkable" src="http://lorempixel.com/100/100/city/3" />
Using the imgCheckbox jQuery Plugin:
Inspired by the solution above, I have built a plugin which can be used as easily as:
$("img").imgCheckbox();
Injects the data for checked images into your form
Supports custom check marks
Supports customised CSS
Supports preselected elements
Supports radio groups instead of simple toggling of images
Has event callbacks
Sensible defaults
Lightweight and super easy to use
See it in action (and see the source)
I would append an extra div with position: relative; and class="checked" which has the same width/height as the image has and than position in left: 0; top: 0; containing the icon. It starts with display: none;.
Now you can listen to the click-event:
$( '.captcha_images' ).click( function() {
$(this + '.checked').css( 'display', 'block' );
$(this).animate( { width: '70%', height: '70%' } );
});
This way you can get the icon and also resize the image to a smaller way.
Notice: Just wanted to show you the "logic" behind my thoughts, this example might not work or has some bugs in it.
I made an example of "Angular - Use images like checkboxes and radios"
Link to Stackblitz
In app.component.html file:
<div class="cont-bg">
<h5 class="text-white">Checkbox</h5>
<div class="cont-main">
<div class="cont-checkbox" *ngFor="let car of cars; index as i">
<input type="checkbox" [id]="'myCheckbox-' + i" />
<label [for]="'myCheckbox-' + i">
<img [src]="car.img" />
<span class="cover-checkbox">
<svg viewBox="0 0 12 10">
<polyline points="1.5 6 4.5 9 10.5 1"></polyline>
</svg>
</span>
<div class="info">{{ car.name }}</div>
</label>
</div>
</div>
<h5 class="text-white">Radio</h5>
<div class="cont-main">
<div class="cont-checkbox" *ngFor="let car of cars; index as i">
<input type="radio" name="myRadio" [id]="'myRadio-' + i" />
<label [for]="'myRadio-' + i">
<img [src]="car.img" />
<span class="cover-checkbox">
<svg viewBox="0 0 12 10">
<polyline points="1.5 6 4.5 9 10.5 1"></polyline>
</svg>
</span>
<div class="info">{{ car.name }}</div>
</label>
</div>
</div>
</div>
In app.component.scss file:
* {
font-family: Lato;
--transition: 0.15s;
--border-radius: 0.5rem;
--background: #ffc107;
--box-shadow: #ffc107;
// --box-shadow: #0082ff;
}
.cont-bg {
min-height: 100vh;
background-image: radial-gradient(
circle farthest-corner at 7.2% 13.6%,
rgba(37, 249, 245, 1) 0%,
rgba(8, 70, 218, 1) 90%
);
padding: 1rem;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.cont-main {
display: flex;
flex-wrap: wrap;
align-content: center;
justify-content: center;
}
.cont-checkbox {
width: 150px;
height: 100px;
border-radius: var(--border-radius);
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
transition: transform var(--transition);
background: white;
margin-bottom: 0.75rem;
margin-right: 0.75rem;
&:active {
transform: scale(0.9);
}
input {
display: none;
&:checked + label {
opacity: 1;
box-shadow: 0 0 0 3px var(--background);
.cover-checkbox {
opacity: 1;
transform: scale(1);
svg {
stroke-dashoffset: 0;
}
}
img {
-webkit-filter: none; /* Safari 6.0 - 9.0 */
filter: none;
}
}
}
label {
cursor: pointer;
border-radius: var(--border-radius);
overflow: hidden;
width: 100%;
height: 100%;
position: relative;
opacity: 0.6;
img {
width: 100%;
height: 70%;
object-fit: cover;
clip-path: polygon(0% 0%, 100% 0, 100% 81%, 50% 100%, 0 81%);
-webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */
filter: grayscale(100%);
}
.cover-checkbox {
position: absolute;
right: 5px;
top: 3px;
z-index: 1;
width: 23px;
height: 23px;
border-radius: 50%;
background: var(--box-shadow);
border: 2px solid #fff;
transition: transform var(--transition),
opacity calc(var(--transition) * 1.2) linear;
opacity: 0;
transform: scale(0);
svg {
width: 13px;
height: 11px;
display: inline-block;
vertical-align: top;
fill: none;
margin: 5px 0 0 3px;
stroke: #fff;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 16px;
transition: stroke-dashoffset 0.4s ease var(--transition);
stroke-dashoffset: 16px;
}
}
.info {
text-align: center;
margin-top: 0.2rem;
font-weight: 600;
font-size: 0.8rem;
}
}
}
In app.component.ts file:
import { Component, VERSION } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
cars = [
{
id: '1',
name: 'Mazda MX-5 Miata',
img: 'https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/2021-mazda-mx-5-miata-mmp-1-1593459650.jpg?crop=0.781xw:0.739xh;0.109xw,0.0968xh&resize=480:*',
},
{
id: '2',
name: 'Toyota Supra',
img: 'https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/2020-chevrolet-corvette-c8-102-1571146873.jpg?crop=0.548xw:0.411xh;0.255xw,0.321xh&resize=980:*',
},
{
id: '3',
name: 'Chevy Corvette',
img: 'https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/2021-porsche-cayman-mmp-1-1593003674.jpg?crop=0.648xw:0.485xh;0.129xw,0.263xh&resize=980:*',
},
{
id: '4',
name: 'Porsche 718 Cayman',
img: 'https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/2021-porsche-718-cayman-mmp-1-1593003156.jpg?crop=0.735xw:0.551xh;0.138xw,0.240xh&resize=980:*',
},
{
id: '5',
name: 'Porsche 718 Boxster',
img: 'https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/2021-bmw-m2-mmp-1-1599687968.jpg?crop=0.706xw:0.528xh;0.102xw,0.268xh&resize=980:*',
},
{
id: '6',
name: 'BMW M2',
img: 'https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/2021-bmw-z4-mmp-1-1599583762.jpg?crop=0.779xw:0.584xh;0.0782xw,0.196xh&resize=980:*',
},
{
id: '7',
name: 'BMW Z4',
img: 'https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/2021-chevrolet-camaro-mmp-1-1585333717.jpg?crop=0.344xw:0.331xh;0.241xw,0.328xh&resize=980:*',
},
{
id: '8',
name: 'Chevy Camaro',
img: 'https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/2021-chevrolet-camaro-zl1-mmp-1-1604071262.jpg?crop=0.818xw:0.663xh;0.0799xw,0.163xh&resize=980:*',
},
{
id: '9',
name: 'Chevy Camaro ZL1',
img: 'https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/2021-chevrolet-camaro-zl1-mmp-1-1604071262.jpg?crop=0.818xw:0.663xh;0.0799xw,0.163xh&resize=768:*',
},
];
}
Pure CSS Checkbox and Radio:
* {
font-family: Lato;
margin: 0;
padding: 0;
--transition: 0.15s;
--border-radius: 0.5rem;
--background: #ffc107;
--box-shadow: #ffc107;
}
.cont-bg {
min-height: 100vh;
background-image: radial-gradient(
circle farthest-corner at 7.2% 13.6%,
rgba(37, 249, 245, 1) 0%,
rgba(8, 70, 218, 1) 90%
);
padding: 1rem;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.cont-title {
color: white;
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 1rem;
}
.cont-main {
display: flex;
flex-wrap: wrap;
align-content: center;
justify-content: center;
}
.cont-checkbox {
width: 150px;
height: 100px;
border-radius: var(--border-radius);
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
background: white;
transition: transform var(--transition);
}
.cont-checkbox:first-of-type {
margin-bottom: 0.75rem;
margin-right: 0.75rem;
}
.cont-checkbox:active {
transform: scale(0.9);
}
input {
display: none;
}
input:checked + label {
opacity: 1;
box-shadow: 0 0 0 3px var(--background);
}
input:checked + label img {
-webkit-filter: none; /* Safari 6.0 - 9.0 */
filter: none;
}
input:checked + label .cover-checkbox {
opacity: 1;
transform: scale(1);
}
input:checked + label .cover-checkbox svg {
stroke-dashoffset: 0;
}
label {
display: inline-block;
cursor: pointer;
border-radius: var(--border-radius);
overflow: hidden;
width: 100%;
height: 100%;
position: relative;
opacity: 0.6;
}
label img {
width: 100%;
height: 70%;
object-fit: cover;
clip-path: polygon(0% 0%, 100% 0, 100% 81%, 50% 100%, 0 81%);
-webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */
filter: grayscale(100%);
}
label .cover-checkbox {
position: absolute;
right: 5px;
top: 3px;
z-index: 1;
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--box-shadow);
border: 2px solid #fff;
transition: transform var(--transition),
opacity calc(var(--transition) * 1.2) linear;
opacity: 0;
transform: scale(0);
}
label .cover-checkbox svg {
width: 13px;
height: 11px;
display: inline-block;
vertical-align: top;
fill: none;
margin: 5px 0 0 3px;
stroke: #fff;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 16px;
transition: stroke-dashoffset 0.4s ease var(--transition);
stroke-dashoffset: 16px;
}
label .info {
text-align: center;
margin-top: 0.2rem;
font-weight: 600;
font-size: 0.8rem;
}
<div class="cont-bg">
<div class="cont-title">Checkbox</div>
<div class="cont-main">
<div class="cont-checkbox">
<input type="checkbox" id="myCheckbox-1" />
<label for="myCheckbox-1">
<img
src="https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/2021-mazda-mx-5-miata-mmp-1-1593459650.jpg?crop=0.781xw:0.739xh;0.109xw,0.0968xh&resize=480:*"
/>
<span class="cover-checkbox">
<svg viewBox="0 0 12 10">
<polyline points="1.5 6 4.5 9 10.5 1"></polyline>
</svg>
</span>
<div class="info">Mazda MX-5 Miata</div>
</label>
</div>
<div class="cont-checkbox">
<input type="checkbox" id="myCheckbox-2" />
<label for="myCheckbox-2">
<img
src="https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/2020-chevrolet-corvette-c8-102-1571146873.jpg?crop=0.548xw:0.411xh;0.255xw,0.321xh&resize=980:*"
/>
<span class="cover-checkbox">
<svg viewBox="0 0 12 10">
<polyline points="1.5 6 4.5 9 10.5 1"></polyline>
</svg>
</span>
<div class="info">Toyota Supra</div>
</label>
</div>
</div>
<div class="cont-title">Radio</div>
<div class="cont-main">
<div class="cont-checkbox">
<input type="radio" name="myRadio" id="myRadio-1" />
<label for="myRadio-1">
<img
src="https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/2021-mazda-mx-5-miata-mmp-1-1593459650.jpg?crop=0.781xw:0.739xh;0.109xw,0.0968xh&resize=480:*"
/>
<span class="cover-checkbox">
<svg viewBox="0 0 12 10">
<polyline points="1.5 6 4.5 9 10.5 1"></polyline>
</svg>
</span>
<div class="info">Mazda MX-5 Miata</div>
</label>
</div>
<div class="cont-checkbox">
<input type="radio" name="myRadio" id="myRadio-2" />
<label for="myRadio-2">
<img
src="https://hips.hearstapps.com/hmg-prod.s3.amazonaws.com/images/2020-chevrolet-corvette-c8-102-1571146873.jpg?crop=0.548xw:0.411xh;0.255xw,0.321xh&resize=980:*"
/>
<span class="cover-checkbox">
<svg viewBox="0 0 12 10">
<polyline points="1.5 6 4.5 9 10.5 1"></polyline>
</svg>
</span>
<div class="info">Toyota Supra</div>
</label>
</div>
</div>
</div>
I've noticed other answers either don't use <label> (why not?), or require matching for and id attributes. This means if you have clashing IDs, your code won't work, and you must remember to make unique IDs each time.
Also, if you hide the input with display:none or visibility:hidden, the browser will not focus on it.
A checkbox and its text (or in this case, image) can be wrapped in a label:
.fancy-checkbox-label > input[type=checkbox] {
position: absolute;
opacity: 0;
cursor: inherit;
}
.fancy-checkbox-label {
font-weight: normal;
cursor: pointer;
}
.fancy-checkbox:before {
font-family: FontAwesome;
content: "\f00c";
background: #fff;
color: transparent;
border: 3px solid #ddd;
border-radius: 3px;
z-index: 1;
}
.fancy-checkbox-label:hover > .fancy-checkbox:before,
input:focus + .fancy-checkbox:before {
border-color: #bdbdff;
}
.fancy-checkbox-label:hover > input:not(:checked) + .fancy-checkbox:before {
color: #eee;
}
input:checked + .fancy-checkbox:before {
color: #fff;
background: #bdbdff;
border-color: #bdbdff;
}
.fancy-checkbox-img:before {
position: absolute;
margin: 3px;
line-height: normal;
}
input:checked + .fancy-checkbox-img + img {
transform: scale(0.9);
box-shadow: 0 0 5px #bdbdff;
}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-T8Gy5hrqNKT+hzMclPo118YTQO6cYprQmhrYwIiQ/3axmI1hQomh7Ud2hPOy8SP1" crossorigin="anonymous">
<p>
<label class="fancy-checkbox-label">
<input type="checkbox">
<span class="fancy-checkbox"></span>
A normal checkbox
</label>
</p>
<p>
<label class="fancy-checkbox-label">
<input type="checkbox">
<span class="fancy-checkbox fancy-checkbox-img"></span>
<img src="https://picsum.photos/seed/1/100">
</label>
</p>
Here a quick example of selecting an image like a checkbox
Updated Example using Knockout.js:
var imageModel = function() {
this.chk = ko.observableArray();
};
ko.applyBindings(new imageModel());
input[type=checkbox] {
display:none;
}
input[type=checkbox] + label
{
display:inline-block;
width:150px;
height:150px;
background:#FBDFDA;
border:none;
}
input[type=checkbox]:checked + label
{
background:#CFCFCF;
border:none;
position:relative;
width:100px;
height:100px;
padding: 20px;
}
input[type=checkbox]:checked + label:after
{
content: '\2713';
position:absolute;
top:-10px;
right:-10px;
border-radius: 10px;
width: 25px;
height: 25px;
border-color: white;
background-color: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.0.0/knockout-min.js"></script>
<input type='checkbox' name='image1' value='image1' id="image1" data-bind="checked: chk"/><label for="image1"></label><label for="image1"><img class='testbtn'/></label>
<div data-bind="html: chk"></div>
To expand on the accepted answer for anyone using WordPress & GravityForms to generate their forms and wish to automatically populate checkbox fields with a list of posts and their associated Featured Thumbnail
// Change '2' to your form ID
add_filter( 'gform_pre_render_2', 'populate_checkbox' );
add_filter( 'gform_pre_validation_2', 'populate_checkbox' );
add_filter( 'gform_pre_submission_filter_2', 'populate_checkbox' );
add_filter( 'gform_admin_pre_render_2', 'populate_checkbox' );
function populate_checkbox( $form ) {
foreach( $form['fields'] as &$field ) {
// Change '41' to your checkbox field ID
$field_id = 41;
if ( $field->id != $field_id ) {
continue;
}
// Adjust $args for your post type
$args = array(
'post_type' => 'pet',
'post_status' => 'publish',
'posts_per_page' => -1,
'tax_query' => array(
array(
'taxonomy' => 'pet_category',
'field' => 'slug',
'terms' => 'cat'
)
)
);
$posts = get_posts( $args );
$input_id = 1;
foreach( $posts as $post ) {
$feat_image_url = wp_get_attachment_image( get_post_thumbnail_id( $post->ID ), 'thumbnail' );
$feat_image_url .= '<br />' . $post->post_title;
if ( $input_id % 10 == 0 ) {
$input_id++;
}
$choices[] = array( 'text' => $feat_image_url, 'value' => $post->post_title );
$inputs[] = array( 'label' => $post->post_title, 'id' => "{$field_id}.{$input_id}" );
$input_id++;
}
$field->choices = $choices;
$field->inputs = $inputs;
}
return $form;
}
And the CSS:
.gform_wrapper .gfield_checkbox li[class^="gchoice_2_41_"] {
display: inline-block;
}
.gform_wrapper .gfield_checkbox li input[type="checkbox"][id^="choice_2_41_"] {
display: none;
}
.gform_wrapper .gfield_checkbox li label[id^="label_2_41_"] {
border: 1px solid #fff;
padding: 10px;
display: block;
position: relative;
margin: 10px;
cursor: pointer;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
label[id^="label_2_41_"]:before {
font-family: "font-icons";
font-size: 32px;
color: #1abc9c;
content: " ";
display: block;
background-color: transparent;
position: absolute;
top: -5px;
left: -5px;
width: 25px;
height: 25px;
text-align: center;
line-height: 28px;
transition-duration: 0.4s;
transform: scale(0);
}
label[id^="label_2_41_"] img {
transition-duration: 0.2s;
transform-origin: 50% 50%;
}
:checked + label[id^="label_2_41_"] {
border-color: #ddd;
}
/* FontAwesome tick */
:checked + label[id^="label_2_41_"]:before {
content: "\e6c8";
background-color: transparent;
transform: scale(1);
}
:checked + label[id^="label_2_41_"] img {
transform: scale(0.9);
box-shadow: 0 0 5px #333;
z-index: 0;
}
My goal is to create a to-do list and having the possibility to add more items to the list.
I'm using the .append() method to add content. However, I also need to add a checkbox for every new item added.
Here's what I have so far
(Run the code snippet and add a new item, you'll see the error I'm having)
$(document).ready(function() {
$("#button").click(function() {
var check = $("<input>", { type: "checkbox" });
var toAdd = $("input[name=ListItem]").val();
$("ul").append($("<li>" + check + "<label>" + toAdd + "</label>" + "</li>"));
});
});
#import url('https://fonts.googleapis.com/css?family=Josefin+Sans');
ul {
list-style: none;
margin: 0;
padding: 0;
}
li {
margin: 50px 0;
font-family: 'Josefin Sans', sans-serif;
font-size: 20px;
font-weight: bolder;
}
input {
cursor: pointer;
position: relative;
visibility: hidden;
}
input:after {
border: 3px solid lightblue;
border-radius: 50%;;
content: "";
display: block;
height: 46px;
width: 46px;
text-align: center;
visibility: visible;
transition: all 100ms ease-in-out;
}
input:checked:after {
border: 3px solid lightblue;
color: white;
font-size: 30px;
border-radius: 50%;
content: "✘";
line-height: 43px;
background-color: lightblue;
animation: bounce 300ms ease-in-out forwards;
}
#keyframes bounce {
0% {transform: scale(1, 1);}
70% {transform: scale(1.1, 1.1);}
100% {transform: scale(1, 1);}
}
label {
position: relative;
left: 50px;
top: 18px;!important
}
input[type=text] {
border: 3px solid lightblue;
width: 100px;
height: 50px;
visibility: visible;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="list">
<h1>To do list</h1>
<form name="toDoList">
<input type="text" name="ListItem" />
</form>
<div id="button">Add</div>
<ul>
<li>
<input type="checkbox">
<label for="checkbox">Item1</label>
</li>
<li>
<input type="checkbox">
<label for="checkbox">Item2</label>
</li>
<li>
<input type="checkbox">
<label for="checkbox">Item3</label>
</li>
</ul>
</div>
You were appending Object to a string, and hence the error. Below is the updated working version of your code:
$(document).ready(function() {
$("#button").click(function() {
var toAdd = $("input[name=ListItem]").val();
$("ul").append($("<li><input type='checkbox'><label>" + toAdd + "</label>" + "</li>"));
});
});
#import url('https://fonts.googleapis.com/css?family=Josefin+Sans');
ul {
list-style: none;
margin: 0;
padding: 0;
}
li {
margin: 50px 0;
font-family: 'Josefin Sans', sans-serif;
font-size: 20px;
font-weight: bolder;
}
input {
cursor: pointer;
position: relative;
visibility: hidden;
}
input:after {
border: 3px solid lightblue;
border-radius: 50%;
;
content: "";
display: block;
height: 46px;
width: 46px;
text-align: center;
visibility: visible;
transition: all 100ms ease-in-out;
}
input:checked:after {
border: 3px solid lightblue;
color: white;
font-size: 30px;
border-radius: 50%;
content: "✘";
line-height: 43px;
background-color: lightblue;
animation: bounce 300ms ease-in-out forwards;
}
#keyframes bounce {
0% {
transform: scale(1, 1);
}
70% {
transform: scale(1.1, 1.1);
}
100% {
transform: scale(1, 1);
}
}
label {
position: relative;
left: 50px;
top: 18px;
!important
}
input[type=text] {
border: 3px solid lightblue;
width: 100px;
height: 50px;
visibility: visible;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="list">
<h1>To do list</h1>
<form name="toDoList">
<input type="text" name="ListItem" />
</form>
<div id="button">Add</div>
<ul>
<li>
<input type="checkbox">
<label for="checkbox">Item1</label>
</li>
<li>
<input type="checkbox">
<label for="checkbox">Item2</label>
</li>
<li>
<input type="checkbox">
<label for="checkbox">Item3</label>
</li>
</ul>
</div>
Keeping most of your code as it was, it is enough to fix the .append command, using the functions get(0).outerHTML() as follows:
$("ul").append($("<li>" + check.get(0).outerHTML() + "<label>" + toAdd + "</label>" + "</li>"));
The function get(0) selects the first (and only) element in the object and .outerHTML() generates HTML from the jQuery object.
Here is the working snippet:
$(document).ready(function() {
$("#button").click(function() {
var check = $("<input>", { type: "checkbox" });
var toAdd = $("input[name=ListItem]").val();
alert(check.get(0).outerHTML);
$("ul").append($("<li>" + check.get(0).outerHTML + "<label>" + toAdd + "</label>" + "</li>"));
});
});
#import url('https://fonts.googleapis.com/css?family=Josefin+Sans');
ul {
list-style: none;
margin: 0;
padding: 0;
}
li {
margin: 50px 0;
font-family: 'Josefin Sans', sans-serif;
font-size: 20px;
font-weight: bolder;
}
input {
cursor: pointer;
position: relative;
visibility: hidden;
}
input:after {
border: 3px solid lightblue;
border-radius: 50%;;
content: "";
display: block;
height: 46px;
width: 46px;
text-align: center;
visibility: visible;
transition: all 100ms ease-in-out;
}
input:checked:after {
border: 3px solid lightblue;
color: white;
font-size: 30px;
border-radius: 50%;
content: "✘";
line-height: 43px;
background-color: lightblue;
animation: bounce 300ms ease-in-out forwards;
}
#keyframes bounce {
0% {transform: scale(1, 1);}
70% {transform: scale(1.1, 1.1);}
100% {transform: scale(1, 1);}
}
label {
position: relative;
left: 50px;
top: 18px;!important
}
input[type=text] {
border: 3px solid lightblue;
width: 100px;
height: 50px;
visibility: visible;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="list">
<h1>To do list</h1>
<form name="toDoList">
<input type="text" name="ListItem" />
</form>
<div id="button">Add</div>
<ul>
<li>
<input type="checkbox">
<label for="checkbox">Item1</label>
</li>
<li>
<input type="checkbox">
<label for="checkbox">Item2</label>
</li>
<li>
<input type="checkbox">
<label for="checkbox">Item3</label>
</li>
</ul>
</div>
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;
}