Pure Javascript target label - javascript

I have some checkboxes and I want the text of the label to change when the checkbox is selected:
JSFIDDLE
var listener = function() {
document.addEventListener('change', function(e) {
if (e.target.checked) {
e.target.label.className = "option-selected";
}
}
}
}
HTML if you are interested:
<input id="0A" class="individual-checkbox" type="checkbox" value="A">
<label for="0A">A</label>
<br>
<input id="0B" class="individual-checkbox" type="checkbox" value="B">
<label for="0B">B</label>
Obviously, target.label doesn't work. How do I access the label of the target and give it a CSS class (purely in JavaScript)?

To make your jsFiddle work, you have to change the following:
Fix the syntax error (missing a closing paren for the addListener() function call and you have an extra closing brace.
Actually call the listener() function to make it run.
Target the actual label, not the checkbox with your class
There are several different ways to target the label. The simplest would be to enclose the input inside the label and then just use .parentNode to get the label from the checkbox.
HTML:
<label for="0A">
<input id="0A" class="individual-checkbox" type="checkbox" value="A">
A</label>
<br>
<label for="0B">
<input id="0B" class="individual-checkbox" type="checkbox" value="B">
B</label>
code:
var listener = function() {
document.addEventListener('change', function(e) {
if (e.target.checked) {
e.target.parentNode.className = "option-selected";
}
});
}
listener();
Working jsFiddle: http://jsfiddle.net/jfriend00/s2A7W/
If you don't want to change your HTML, then you just need to find the label element that is right after your input element.
You can do that like this:
var listener = function() {
document.addEventListener('change', function(e) {
var label;
if (e.target.checked) {
label = next(e.target, "label");
if (label) {
label.className = "option-selected";
}
}
});
}
listener();
function next(src, tag) {
tag = tag.toUpperCase();
while (src && src.tagName !== tag) {
src = src.nextSibling;
}
return src;
}
Working jsFiddle: http://jsfiddle.net/jfriend00/3wQKa/
FYI, you should probably also restrict the action of your listener to only when a checkbox is actually the target or an element with a particular classname or some other distinguishing feature that makes sure it's a checkbox you want targeted by this code. You are probably safe with e.target.checked, but I don't like the fact that this event listener responds to all propagated change events in the entire page.

Assuming there is only one label element associated with the element:
e.target.labels[0].className = "option-selected";
This is an HTML5 property though, I don't know how well it is supported in older browsers. Source: MDN.
Alternatively, if IE8 support is enough for you, you can explicitly search for it with document.querySelector:
var label = document.querySelector('[for=' + e.target.name + ']');
This only works if you give the input elements name attributes (which you really want to do, otherwise the labels are not properly connected to the input elements).
And finally, if the label always comes after the input, you can traverse the DOM:
var label = e.target.nextSibling;
while (label.nodeName !== 'LABEL') {
label = label.nextSibling;
}
If you'd restructure your HTML so that the input element is a child of the label element:
<label for="0A">
<input id="0A" class="individual-checkbox" type="checkbox" value="A">
A
</label>
then you could simply use e.target.parentNode to get the label. Putting the input element inside the label also connects the label to the input.

this will do the trick:
function addListener(elem) {
// remember: checkboxes never change trair value
elem.addEventListener('click', function () {
var ls = document.getElementsByTagName('label');
for (var l = 0, ll = ls.length, lbl; l < ll; ++l) {
lbl = ls[l];
if ( this.id == lbl.getAttribute("for") )
{
lbl.className = ( this.checked ? "option-selected" : "" );
}
}
});
}
see http://jsfiddle.net/fGSCH/10/
be aware that addListener() might not work in every browser. but since you used it in the example i will use it too.

Related

How to remove inner HTML content onchange

I created a form where a user selects options from a checkbox list. So when a user selects an option in the checkbox, I use a function to show the value of the input field using onchange within inner HTML. My question is, how do we remove that same inner HTML content if the user un-selects those options? So when the user toggles back and forth, it either appears or when un-selected, the value gets removed. Thanks
function functionOne() {
var x = document.getElementById("wheels").value;
document.getElementById("demo").innerHTML = x;
}
<input type="checkbox" id="wheels" onchange="functionOne()" value="feature 1">
<div id="demo"></div>
Check the state of the checkbox before you read the value.
function functionOne(cb) {
var x = cb.checked ? cb.value : '';
document.getElementById("demo").innerHTML = x;
}
<input type="checkbox" id="wheels" onchange="functionOne(this)" value="feature 1">
<div id="demo"></div>
Inside the change function on deselect do this:
document.getElementById("demo").innerHTML = '';
The element that is changed has a checked property which can be inspected - it will be either true or false. So write an if/else condition to update the content of the demo element depending on its value.
I've adjusted your code slightly to cache the elements outside of the function, and to add an event listener to the checkbox instead of using inline JS which is a bit old-school these days. Also, since the value is just a string textContent is more suitable in this case than innerHTML.
// Cache the elements
const wheels = document.getElementById('wheels');
const demo = document.getElementById('demo');
// Add a listener to the wheels element which calls the
// handler when it changes
wheels.addEventListener('change', handleChange);
// Here `this` refers to the clicked element. If its
// checked property is `true` set the text content of
// `demo` to its value, otherwise use an empty string instead
function handleChange() {
if (this.checked) {
demo.textContent = this.value;
} else {
demo.textContent = '';
}
}
<input type="checkbox" id="wheels" value="feature 1">
<div id="demo"></div>

How do I change the innerHTML of a <form-check form-switch> element in js?

This is my html code:
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="mySwitch" name="emp">
<label class="form-check-label" for="mySwitch">EmpName</label>
</div>
This is my js:
var Switch = document.getElementById("mySwitch");
Switch.addEventListener("click",function(){
if(Switch.value=="yes")
Switch.innerHTML="EmpNo";
else
Switch.innerHTML="EmpName";
})
I don't know why the text isn't changing. Can you please help me?
You're selecting the <input> element, but the one containing the text is the <label> element next to the input.
const input = document.getElementById("mySwitch");
const label = input.nextElementSibling.
Listen for the change event on the input instead of click. The change event will be triggered whenever the input has been checked or unchecked and is more reliable for this case.
Then check the .checked property on the input to see of the checkbox has been checked or not. Instead of modifying the .innerHTML property, use .textContent to set the changed text.
input.addEventListener('change' function() {
if (input.checked) {
label.textContent = 'EmpNo';
} else {
label.textContent = 'EmpName';
}
});

Enter Key on Input Automatically Tab to Next Input with Fields in iFrames

I have a form with inputs which also has an iFrame embedded in the form which also has inputs (pseudo HTML):
<input type="text" name="one" value="one" />
<input type="text" name="two" value="two" />
<input type="text" name="three" value="three" />
<iframe
<input type="text" name="bacon" value="bacon">
</iframe>
<input type="text" name="four" value="four" />
<input type="text" name="five" value="five" />
When the user presses tab they are taken from input to input even inside the iframe fields selecting bacon after three. We all love bacon.
I also have some javascript that attempts to focus the next input on enter key:
$(document).ready(function() {
$(document).on('keydown', 'input', function(ev) {
// Move to next on enter
if (ev.which === 13) {
var inputs = $(':tabbable');
var next = inputs.index(this) + 1;
var input = inputs.eq(next == inputs.length ? 0 : next);
input.focus();
return false;
}
});
});
The problem is the javascript enter key code never focuses the bacon field, it will skip right over it. jsfiddle:
https://jsfiddle.net/54n8mqkh/4/
Let's all skip answers that include not using the iFrame. I know it is not an ideal implementation. However, I would accept ANY answer that allows the enter key to move through all the fields consistently including the iframe using any type of javascript. It does not have to be jquery specific.
I have tried a few different approaches to solve this but none I have found works. Thanks in advance to anyone who has a solution.
You need to focus inside of the iframe like so :
var frameBody = $("#IFrame_input").contents().find("input");
frameBody.focus();
I am going to answer my own question - after a few more hours I was able to solve my use case by expanding my selector to include the iframe. Then I build the array of inputs manually and while iterating I checked the node type for an iframe. If / when I encountered an iframe, I did the same select inside the iframe and added the inputs within the iframe:
$(document).ready(function() {
// Parent level helper function
window.tabToNext = function(me) {
var selector = ':tabbable, iframe';
var inputElems = [];
$(selector).each(function(index) {
var nodeName = $(this).prop('nodeName').toLowerCase();
if (nodeName == 'iframe') {
$(this).contents().find(selector).each(function(index) {
inputElems.push(this);
});
} else {
inputElems.push(this);
}
});
var inputs = $(inputElems);
var next = inputs.index(me) + 1;
if (next == inputs.length) next = 0;
var input = inputs.eq(next);
input.focus();
return false;
}
$(document).on('keydown', 'input', function(ev) {
// Move to next on enter
if (ev.which === 13) {
return window.tabToNext(this);
}
});
// Focus the first input
$('input[name=one]').focus();
});
FWIW: I could not just expand the selector as best I could tell and also I tried to use $.add to build the collection starting with an empty jQuery collection:
var foo = $([]);
foo.add(someElement);
... but it does not honor the order you add. It will re-order to the DOM according to the docs which SEEMS like it should be right, but for some reason my iframe child fields always ended up last and messed up the tab order.
Anyhow, I hope if someone else has this issue some day you find this helpful. Working solution:
https://jsfiddle.net/wbs1zajs/6/

Why does the browser stop displaying checkbox updates?

I'm using jQuery to help select a some matching checkbox elements in a form. The form is created dynamically, but here is a stripped down version of it that illustrated my point:
http://jsfiddle.net/phinor/qj7uj3rc/
This is the relevant JavaScript:
$(document).ready (function () {
$('.lessoncount').on ('click', tickAlike);
});
function tickAlike ()
{
var classes = $(this).attr('data-classes');
if ($(this).siblings ('.tick_' + classes).attr ('checked'))
{
console.log ('ticking');
$('.tick_' + classes).removeAttr ('checked');
}
else
{
console.log ('unticking');
$('.tick_' + classes).attr ('checked', true);
}
}
The idea is that checkboxes can be individually selected or, if the "label" is clicked on, all checkboxes with the same class will be selected or deselected to match the box next to it. Because I want two possible behaviours depending on whether they click on the box or the label, I cannot use the <label> tag. As a follow on from that, because of the independent selection, "toggling" is not viable.
This seems to work for one cycle of "ticks" and "unticks" and then stops. If I inspect the checkbox element in Chrome, I can see that the "checked" attribute is being added and removed appropriately, but the browser view is no longer updated and the ticks do not show.
This is consistent in the lastest versions of Chrome, Firefox and IE, so I'm guessing that I'm doing something wrong!
You should be using .prop() instead of .attr() when modifying boolean attributes, e.g. checked, disabled, readonly, selected and etc. Using .removeAttr() or .removeProp() will cause the attribute to be removed forever, and cannot be further manipulated. To understand the difference between .prop() and .attr(), there is actually a very good question (with a well-structured answer) asked on SO before.
To programmatically check a checkbox, simply use $selector.prop('checked',true). To uncheck, use $selector.prop('checked',false).
Also, to check if a checkbox is checked (no pun intended), you can use the .is() method to do so, i.e. $selector.is(':checked').
$(document).ready(function () {
$('.lessoncount').on('click', tickAlike);
});
function tickAlike ()
{
var classes = $(this).attr('data-classes');
if ($(this).siblings('.tick_' + classes).is(':checked'))
{
console.log('ticking');
$('.tick_' + classes).prop('checked', false);
}
else
{
console.log('unticking');
$('.tick_' + classes).prop('checked', true);
}
}
A working demo: http://jsfiddle.net/teddyrised/qj7uj3rc/5/
Even better: I can see that the <span> element containing the description for each checkbox is semantically unhinged from its accompanying checkbox, i.e. there is no way to give context to each checkbox. The <label> element is created for this purpose.
You might still want to style your descriptors independently, so we can keep them wrapped in <span>, but we can wrap all the content within each <td> with the <label> element, so that a click on both the text and the checkbox will trigger the same response.
A revised markup for the rows in the table body would look something like this:
<tr>
<td>1-Mo</td>
<td><label for="lesson_1_1"><input type="checkbox" name="lesson[1][1]" value="1" id="lesson_1_1" class="tick_1645_1646_1647_1648_1649_1650_2024"/> <span data-classes="1645_1646_1647_1648_1649_1650_2024" class="lessoncount">(7)</span></label></td>
<td><label for="lesson_1_2"><input type="checkbox" name="lesson[1][2]" value="1" id="lesson_1_2" class="tick_1582_1583_1584_1585_1586_1587"/> <span data-classes="1582_1583_1584_1585_1586_1587" class="lessoncount">(6)</span></label></td>
<td><label for="lesson_1_3"><input type="checkbox" name="lesson[1][3]" value="1" id="lesson_1_3" class="tick_1658_1673_1684_1700_1706_1736_1737_1769"/> <span data-classes="1658_1673_1684_1700_1706_1736_1737_1769" class="lessoncount">(8)</span></label></td>
<td><label for="lesson_1_4"><input type="checkbox" name="lesson[1][4]" value="1" id="lesson_1_4" class="tick_1602_1603_1604_1605_1618_1628"/> <span data-classes="1602_1603_1604_1605_1618_1628" class="lessoncount">(6)</span></label></td>
<td><label for="lesson_1_5"><input type="checkbox" name="lesson[1][5]" value="1" id="lesson_1_5" class="tick_"/> <span>(0)</span></label></td>
<td><label for="lesson_1_6"><input type="checkbox" name="lesson[1][6]" value="1" id="lesson_1_6" class="tick_1659_1674_1701_1738_1755_1756"/> <span data-classes="1659_1674_1701_1738_1755_1756" class="lessoncount">(6)</span></label></td>
<td><label for="lesson_1_7"><input type="checkbox" name="lesson[1][7]" value="1" id="lesson_1_7" class="tick_1719_1720_1721_1722_1723_2003"/> <span data-classes="1719_1720_1721_1722_1723_2003" class="lessoncount">(6)</span></label></td>
<td>detail | clear</td>
</tr>
With a slightly modified script to listen to the click even on the wrapping <label> element instead of a text span:
$(document).ready(function () {
$('table tbody label').on('click', tickAlike);
});
function tickAlike ()
{
var classes = $(this).find('span.lessoncount').attr('data-classes');
if ($(this).find('input[type="checkbox"]').is(':checked'))
{
console.log('ticking');
$('.tick_' + classes).prop('checked', false);
}
else
{
console.log('unticking');
$('.tick_' + classes).prop('checked', true);
}
}
See alternative working demo: http://jsfiddle.net/teddyrised/pb68gk2n/
The problem is that you should not manipulate with attributes when you want to toggle properties checked.
Here is updated and simplified code:
$(document).ready(function () {
$('.lessoncount').on('click', tickAlike);
});
function tickAlike() {
var classes = $(this).data('classes');
var $checkbox = $(this).siblings('.tick_' + classes).click();
$('.tick_' + classes).prop('checked', $checkbox.is(':checked'));
}
Demo: http://jsfiddle.net/qj7uj3rc/6/
However this is still not ideal: note how clicking on checkbox itself does nothing. So it can be improved. You need to use label instead of span and wrap checkboxes into those labels. So HTML should be this:
<label data-classes="1645_1646_1647_1648_1649_1650_2024" class="lessoncount">
<input type="checkbox" name="lesson[1][1]" value="1" id="lesson_1_1" class="tick_1645_1646_1647_1648_1649_1650_2024" /> (7)
</label>
and after that bind onchange event on checkboxes instead of spans:
$(document).ready(function () {
$('.lessoncount :checkbox').on('change', tickAlike);
});
function tickAlike() {
var classes = $(this).parent().data('classes');
$('.tick_' + classes).not(this).prop('checked', this.checked);
}
Use "is(':checked')" for the conditional and use ".prop ('checked', false)" and ".prop ('checked', true)" in order to make changes in the check input.
Here is the link https://jsfiddle.net/qj7uj3rc/7/
if ($(this).siblings ('.tick_' + classes).is(':checked'))
{
console.log ('ticking');
$('.tick_' + classes).prop ('checked', false);
}
else
{
console.log ('unticking');
$('.tick_' + classes).prop ('checked', true);
}
If your form is created dynamically then it might behave differently than static. Use bind or live instead of click for dynamically created elements:
$(".lessoncount" ).live( "click", function() {
tickAlike();
});

JQuery Custom Radio Buttons code (code not working)

usual but I need to have different custom radio button images per button.
So Radio1 would have different images to Radio2.
Trying it out on the code below but it won't work so I must be doing something wrong?
Here's the code:
<label for="radio1">
<img src="radio1_unchecked.png" style="vertical-align:middle" />
<input name="radiogroup" type="radio" id="radio1" style="display:none;">
</label>
<label for="radio2">
<img src="radio2_unchecked.png" style="vertical-align:middle" />
<input name="radiogroup" type="radio" id="radio2" style="display:none;">
</label>
<script>
$(document).ready(function(){
var radio1checkedImage = "radio1_checked.png",
radio1uncheckedImage = "radio1_unchecked.png",
radio2checkedImage = "radio2_checked.png",
radio2uncheckedImage = "radio2_unchecked.png";
$('img').attr("src", radio1uncheckedImage);
$('#radio1, #radio2').change(function() {
var r;
r = $("#radio1");
r.prev().attr("src", r[0].checked ? radio1checkedImage : radio1uncheckedImage);
r = $("#radio2");
r.prev().attr("src", r[0].checked ? radio2checkedImage : radio2uncheckedImage);
});
});
</script>
Update: Here is the same code as above but without the multiple images.
As you can see it works. Can't the code be modified to have multiple images per radio?
You could use CSS to define which images goes with which radio button. Building on the JSBin example from my answer to you previous question, you can use JavaScript to add a classname (e.g. 'checked') to the parent of the checked radio (i.e. the <label>):
var radios = $('input:radio');
radios.change(function() {
radios.filter(':checked').parent().addClass('checked');
radios.filter(':not(:checked)').parent().removeClass('checked');
});
So, now that the <label> will have the 'checked' class if that radio is selected, you can use CSS to style it:
label {
/* regular styles */
}
label[for="radio1"].checked {
/* checked styles for #radio1's label */
}
label[for="radio2"].checked {
/* checked styles for #radio2's label */
}
Using CSS instead of <img> tags does mean you will need to use background-image, so be aware of that.
Live example: http://jsbin.com/ebapov/edit#javascript,html,live
Just in case, here is a more verbose version of the JavaScript posted above:
// Fetch the radio buttons (this is a jQuery collection):
var radios = $('input:radio');
radios.change(function() {
// Filter the radio inputs into 'checked' and 'unchecked':
var checkedInputs = radios.filter(':checked');
var uncheckedInputs = radios.filter(':not(:checked)');
// Get the 'checked' and 'unchecked' labels:
var checkedLabels = checkedInputs.parent();
var uncheckedLabels = uncheckedInputs.parent();
// Add the class "checked" to the checked labels:
checkedLabels.addClass('checked');
// ... and remove it from the unchecked labels:
uncheckedLabels.removeClass('checked');
});
Most of this code relies on the fact that jQuery functions can be chained. This means that when you call the parent() function on a jQuery collection, it will actually return a collection containing the parent of each of the elements in the original collection. If you then call addClass on that new collection, it will add a classname to each of those parents.
Most of jQuery's functions can be chained.
In addition my other answer, which uses CSS, let me offer an alternative solution.
Firstly; you want to keep track of which image goes with which radio button. Why not use an object literal?
var checkedImages = {
'radio1': "radio1_checked.png",
'radio2': "radio2_checked.png"
};
var uncheckedImage = "unchecked.png";
This way you can easily refer to the different URL's like, e.g.: checkedImages['radio2'].
The change event handler would look very similar. The only difference is what you do with the checkedLabels and the uncheckedLabels:
var radios = $('input:radio');
radios.change(function() {
var checkedLabels = radios.filter(':checked').parent();
var uncheckedLabels = radios.filter(':not(:checked)').parent();
uncheckedLabels.children('img').attr('src', uncheckedImage);
checkedLabels.each(function() {
var image = $(this).children('img');
var name = $(this).attr('for');
if (checkedImages[name] !== undefined) {
// We have checked image for this radio button, so set it:
image.attr('src', checkedImages[name]);
} else {
// We don't have checked image for this radio button.
image.attr('src', uncheckedImage);
}
});
});
The main differences:
We use children() to find the images.
We use each() to add more complicated logic for each of the unchecked label.
We check the selected <label>s for attribute (using .attr('for')) to find out which image we should apply.
In this example only the checked state has different images for the different radio's. If you need different unchecked images as well, you can easily apply the same principle.
Live example: http://jsbin.com/acalir/edit#javascript,html,live
P.S. don't forget to set the src to the unchecked images (in HTML), or do the following (in JS):
$('input:radio').change();
This fires the change event programmatically, which will cause the unchecked images to be applied.

Categories