eventListener always fires on first element - javascript

There are 3 <input type="file"> elements on a page.
Each element has a "change" event listener.
When I click on the 2nd or 3th element, the label of the first is logged to the console.
What am I doing wrong?
Sandbox: https://jsfiddle.net/x01e4kgu/3/
HTML
<form action="">
<div class="input--upload">
<label for="attachment">Upload 1</label>
<input type="file" name="attachment[]" id="attachment">
</div>
<div class="input--upload">
<label for="attachment">Upload 2</label>
<input type="file" name="attachment[]" id="attachment">
</div>
<div class="input--upload">
<label for="attachment">Upload 3</label>
<input type="file" name="attachment[]" id="attachment">
</div>
</form>
JS
const fileInputs = document.querySelectorAll('.input--upload input')
fileInputs.forEach(input => {
input.addEventListener('change', function(e) {
const label = e.target.parentNode.querySelector('label')
console.log(label)
})
})

this way
ID must be UNIQUE (you don't need any one in your code)
all your label elements use the same for attribute pointing on the same id which all concern only the first element having this id (the others are ignored)
use onchange to use an arrow function,
it's allow you to bypass e.target...
because it's allows you to stay within the scope of your local variable(s) (actualy only inEl)
<form action="">
<div class="input--upload">
<label>
Upload 1
<input type="file" name="attachment[]" >
</label>
</div>
<div class="input--upload">
<label>
Upload 2
<input type="file" name="attachment[]" >
</label>
</div>
<div class="input--upload">
<label >
Upload 3
<input type="file" name="attachment[]" ">
</label>
</div>
</form>
const fileInputs = document.querySelectorAll('div.input--upload input')
fileInputs.forEach( inEl =>
{
inEl.onchange = e =>
{
let el_Lb = inEl.closest('label')
console.log( el_Lb.textContent )
}
})

This is happening because they all have the same ids. The for attribute in labels tells that which input they are for. All of the 3 labels are for one input with id attribute attachment and the first occurrence of that is triggered. So in order to get what you need, you will have to assign different ids for each input and change the for in the labels accordingly. Its a common thing to NOT use the same id for more than 1 html element. So the updated code would be:
<form action="">
<div class="input--upload">
<label for="attachment1">Upload 1</label>
<input type="file" name="attachment[]" id="attachment1">
</div>
<div class="input--upload">
<label for="attachment2">Upload 2</label>
<input type="file" name="attachment[]" id="attachment2">
</div>
<div class="input--upload">
<label for="attachment3">Upload 3</label>
<input type="file" name="attachment[]" id="attachment3">
</div>
</form>

Related

How to handle #Html.LabelFor() if i am having 2 of them on the same page with one hidden and one is displayed

Try to click on the word Snacks.
<div hidden>
<form action = "/Test/Index1" method = "post">
<div class="row">
<label for="Snacks">Snacks</label>
<input type="checkbox" id="Snacks">
</div>
</form>
</div>
<div>
<form action = "/Test/Index2" method = "post">
<div class="row">
<label for="Snacks">Snacks</label>
<input type="checkbox" id="Snacks">
</div>
</form>
</div>
On click of that label it will check the 1st checkbox with the id="Snacks", which is inside the hidden div.
To know how it is working remove the hidden attribute of the first div and try it.
You cannot define two id="Snacks" attribute in same page.
You can do something like this
Try to click on the word Snacks.
<div>
<form action = "/Test/Index1" method = "post">
<div class="row">
<label for="Snacks_1">Snacks</label>
<input type="checkbox" id="Snacks_1" name="Snacks">
</div>
</form>
</div>
<div>
<form action = "/Test/Index2" method = "post">
<div class="row">
<label for="Snacks_2">Snacks</label>
<input type="checkbox" id="Snacks_2" name="Snacks">
</div>
</form>
</div>

Access a Particular Div Without Changing Class Name

I have a simple requirement: Based on the response of a user on a particular yes no question, show him another question.
The issue is I am using Bootstrap forms so all my div classes are named form-group, and this is the problem: If I rename the class to say, form-group1, my website shape gets disconfigured...some form items appear out of position.
<script src="http://code.jquery.com/jquery-latest.js" />
<script>
$(document).ready(function () {
$(".form-group1").hide();
$("#r1").click(function () {
$(".form-group1").show();
});
$("#r2").click(function () {
$(".form-group1").hide();
});
});
</script>
<form action="">
<input type="radio" name="gender" id="r1" value="female" onClick="getResults()">Single
<input type="radio" name="gender" id="r2" value="male">Married
<!-- Textarea -->
<div class="form-group1">
<label class="col-md-4 control-label" for="q2">By When is your marriage scheduled?</label>
<div class="col-md-4">
<textarea class="form-control" id="q2" name="Q2" placeholder="e.g. FY18 Q4"></textarea>
</div>
</div>
<br />
<br />
<input type="submit" value="Submit">
</form>
So my question is how can I access the particular div that contains the text-area without changing the class-name?
An element can have as many classes as you like.
So just as an alternative, rather than changing the class name, you can also add another class (separated by a space). That way your bootstrap styles are still applied (since you still have the form-group class), but you can also add your own.
<div class="form-group custom-form-group">
<label class="col-md-4 control-label" for="q2">
By When is your marriage scheduled?
</label>
<div class="col-md-4">
<textarea class="form-control" id="q2" name="Q2" placeholder="e.g. FY18 Q4"></textarea>
</div>
</div>
Then in your script you can target the custom-form group class and it should work as you intended.
You can access the div container by first accessing the ID you know and then traverse:
<input type="radio" name="gender" id="r1" data-div="q2" value="female">Female
<input type="radio" name="gender" id="r2" data-div="q2" value="male">Male
$("[name=gender]").on("click",function() {
$("#"+$(this).data("div")) // textarea with known ID from data attribute
.closest("div.form-group") // the container
.toggle(this.value=="male"); // show or hide
})
You can use DOM relationship to target the desired element, You can traverse up to common ancestor using .closest() then use .find() to get the target element.
$(this).closest("form").find(".form-group").show();
$(document).ready(function() {
$(".form-group").hide();
$("#r1").click(function() {
$(this).closest("form").find(".form-group").show();
});
$("#r2").click(function() {
$(this).closest("form").find(".form-group").hide();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form action="">
<input type="radio" name="gender" id="r1" value="female">Single
<input type="radio" name="gender" id="r2" value="male">Married
<!-- Textarea -->
<div class="form-group">
<label class="col-md-4 control-label" for="q2">By When is your marriage scheduled?</label>
<div class="col-md-4">
<textarea class="form-control" id="q2" name="Q2" placeholder="e.g. FY18 Q4"></textarea>
</div>
</div>
<br><br><input type="submit" value="Submit">
</form>
You can leave the class name unchanged and add an id to the form like this
<div id="form1" class="form-group">...</div>
And then in jQuery do this
$("#form1").show(); // showing the form
$("#form1").hide(); // hiding the form

HTML FORMS - Passing values to two different forms on the same page

I am having problems trying to pass values to two different forms on the same page. The page is populated with 16 radio buttons (which I’ve turned into boxes for display reasons) and they all hold a value (e.g. 001). Both of the forms jobs are to somehow grab the active/selected radio button value and post it to a PHP file so database changes can be made. Instead of a submit button, I am using an anchor to pass a JavaScript submit function. I have tried having
both forms cover the whole page but still didn't have any luck.
I’ll post some code below to help you understand.
PS. If you need more code to understand, I can post it to pastebin.
<li>
<form id="form_ready" method="post" action="../backend/screen.php">
<input type="hidden" name="screenid" value="" />
<input type="hidden" name="status" value="" />
<input type="hidden" name="pickupid" value="document.activeElement.value;" />
<a onclick="document.getElementById('form_ready').submit();">READY</a>
</form>
</li>
<li>
<form id="form_c" method="post" action="../backend/screen.php">
<input type="hidden" name="status" value="" />
<input type="hidden" name="pickupid" value="document.activeElement.value;" />
<a onclick="document.getElementById('form_c').submit();">COLLECTED</a>
</form>
</li>
</ul>
<div id="content">
<div id="container">
<div id="table">
<div id="tr1">
<div id="td1">
<input type="radio" name="pickup" id="1" value="001" />
<label for="1"> <span>001</span> </label>
</div>
<div id="td2">
<input type="radio" name="pickup" id="2" value="002" />
<label for="2"> <span>002</span> </label>
</div>
<div id="td3">
<input type="radio" name="pickup" id="3" value="003" />
<label for="3"> <span>003</span> </label>
</div>
<div id="td4">
<input type="radio" name="pickup" id="4" value="004" />
<label for="4"> <span>004</span> </label>
</div>
To answer the suggestion in the comments and use only one form, here's how to grab the value from the selected radio button:
var inputs = document.getElementsByTagName('input');
var valueSelected;
for (var i = 0; i < inputs.length; i++) {
if (inputs[i].checked) valueSelected = inputs[i].value;
}
valueSelected will contain the value of the selected radio.
If you ever need to reduce the type to "radio" only for some reason, you can check an input type with inputs[i]['type'] === 'radio' in the for loop.
The best would probably still be to set a class for your "radio" inputs, it will allow the loop to be more specific, hence a more efficient code, for example:
<input type="radio" class="myRadio">
and in JS: var inputs = document.getElementsByClassName('myRadio');

Usage of radio value in ID selector

Given this html:
<form action="">
<div id="choices">
<input type="radio" name="stype" id="opt1" value="input1_div" checked=""/> Opt1
<input type="radio" name="stype" id="opt2" value="input2_div"/> Opt2
</div>
<div id="stypes">
<div id="input1_div">
<input type="text" id="input1" name="input1" placeholder="input1"/>
</div>
<div id="input2_div" style="display: none;">
<input type="text" id="input2" name="input2" placeholder="input2" disabled=""/>
</div>
</div>
<div id="#sbutton">
<input type="submit" id="input3" value="Submit"/>
</div>
</form>
I use following jQuery to hide/disable input fields based on selected radio buttons:
jQuery('#choices input').each(function() {
var item = this;
$(this).click(function() {
if($('input[type=radio][name=stype]').is(':checked')) {
$('#stypes > div').hide();
$('#stypes input').not("#sbutton").prop('disabled', true);
$('#' + $(item).val()).fadeIn();
$('#' + $(item).val() + ' input').prop('disabled', false);
}
});
});
All-in-One in this jsfiddle.
I'm particularly unsure about my technique to incorporate radio value into the id selector:
$('#' + $(item).val()).fadeIn();
$('#' + $(item).val() + ' input').prop('disabled', false);
What is the correct way to do it? Other tips regarding my jQuery?
First of all, you don't need a sophisticated jQuery.each loop to bind the click and to define this. In each event handler this will be the caller of the function. In your case it is enough to have click function for all of them.
As you said in comments, it's only matter of presentation. I prefer to have one field instead of two fields. Even I prefer to have a fixed name for this text input, though in order to make it very similar to your example I change the name and placeholder accordingly.
$(function(){
$('#choices input').click(function() {
var newName = $(this).val(),
newPlaceholder = $(this).attr("data-placeholder");
$('#interchangable_input[name!='+newName+']').hide()
.attr("name", newName)
.attr("placeholder", newPlaceholder)
.fadeIn();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form action="">
<div id="choices">
<input type="radio" name="stype" id="opt1" data-placeholder="Input one" value="input1" checked=""/> <label for="opt1">Opt1</label>
<input type="radio" name="stype" id="opt2" data-placeholder="Input two" value="input2"/> <label for="opt2">Opt2</label>
</div>
<div id="stypes">
<input type="text" id="interchangable_input" name="input1" placeholder="Input one"/>
</div>
<div id="#sbutton">
<input type="submit" id="input3" value="Submit"/>
</div>
</form>
Few more suggestions:
You can use <label for="target id"> for each option label in radio buttons or checkboxes. It will work fine with click event handler, even if the user clicks on the label instead of the button itself.
You can hid some information as data-* attribute in html5. You don't need to necessarily use value.
Update for multiple items
In case of group of multiple items in form, I can imagine a form with different sections or pages. The <fieldset> can be use to group these items. In HTML5 you could just disable the fieldset tag by <fieldset disabled> when you change the form page with jquery. (With exception of IE browsers). Because of this exception we need to disable all items in subforms. In order to handle this part, I defined a class .form-element which applies for all form inputs. You can also use <div> instead of <fieldset>, then you will need to track their enable/disable status.
$(function(){
// initialization by disabling form-elements and hiding them
$("#subforms fieldset[disabled]").hide();
$("#subforms fieldset[disabled] .form-element").attr('disabled','disabled');
// choice tracker
$('#choices input').click(function() {
var $target = $($(this).attr("data-subform")),
$oldElement = $("#subforms fieldset:not([disabled])");
$oldElement.attr('disabled','disabled').hide();
$oldElement.find(".form-element").attr('disabled','disabled');
$target.removeAttr("disabled");
$target.find(".form-element").removeAttr("disabled");
$target.fadeIn();
});
});
fieldset {
border: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form action="">
<div id="choices">
<input type="radio" name="stype" id="opt1" data-subform="#subform1" value="subform1" checked=""/> <label for="opt1">Opt1</label>
<input type="radio" name="stype" id="opt2" data-subform="#subform2" value="subform2"/> <label for="opt2">Opt2</label>
</div>
<div id="subforms">
<fieldset id="subform1" >
<input class="form-element" type="text" name="input1_1" placeholder="Input one"/>
<input class="form-element" type="text" name="input1_2" placeholder="Input two"/>
</fieldset>
<fieldset id="subform2" disabled>
<input class="form-element" type="text" name="input2_1" placeholder="Input one"/><br />
<textarea class="form-element" name="input2_2" placeholder="Input two"></textarea><br />
<input class="form-element" type="checkbox" name="input2_3" id="input2_3" /> <label for="input2_3">check this first</label>
</fieldset>
</div>
<div id="#sbutton">
<input type="submit" id="input3" value="Submit"/>
</div>
</form>

jQuery - Uncheck other checkboxes if a specific checkbox is selected by user

I would like to uncheck all the checkboxes that are presently selected if a specific checkbox is selected by the user.
Example:
<div>
<label for="foo">
<input type="checkbox" name="meh" id="foo" checked> foo
</label>
</div>
<div>
<label for="bar">
<input type="checkbox" name="meh" id="bar" checked> bar
</label>
</div>
<div>
<label for="foobar">
<input type="checkbox" name="meh" id="foobar"> foobar
</label>
</div>
<div>
<label for="barfoo">
<input type="checkbox" name="meh" id="barfoo" checked> barfoo
</label>
</div>
<div>
<label for="omgwtfbbq">
<input type="checkbox" name="meh" id="omgwtfbbq"> omgwtfbbq
</label>
</div>
If the user selects "omgwtfbbq" checkbox, I would like all the other boxes that might be checked to be unchecked and have the "omgwtfbbq" be the only one checked.
for the label instead of id I think you need for
<div>
<label for="foo">
<input type="checkbox" name="foo" id="foo" checked /> foo
</label>
</div>
<div>
<label for="bar">
<input type="checkbox" name="bar" id="bar" checked /> bar
</label>
</div>
<div>
<label for="foobar">
<input type="checkbox" name="foobar" id="foobar" /> foobar
</label>
</div>
<div>
<label for="barfoo">
<input type="checkbox" name="barfoo" id="barfoo" checked /> barfoo
</label>
</div>
<div>
<label for="omgwtfbbq">
<input type="checkbox" name="omgwtfbbq" id="omgwtfbbq" /> omgwtfbbq
</label>
</div>
then
var $others = $('input[type="checkbox"][name="meh"]').not('#omgwtfbbq')
$('#omgwtfbbq').change(function () {
if (this.checked) {
$others.prop('checked', false)
}
});
$others.change(function () {
if (this.checked) {
$('#omgwtfbbq').prop('checked', false)
}
})
Demo: Fiddle
Note: I'll add a common class to all the input elements which has to be affected by omgwtfbbq and change var $others = $('#foo, #bar, #foobar, #barfoo') to var $others = $('.myclassoninput')
Live demo (click).
$('#omgwtfbbq').click(function() {
$('input:checkbox').not(this).attr('checked', false);
});
Also note that you're re-using id's. Id's should only be used once in a document.
If you choose not to give each checkbox a sequential IDs so that you can use an array, here's a solution:
Place all your controls in a div, with an ID "checkgroup".
Then the JavaScript function goes:
function checkone(d){
if (!d.checked) return; //if it's unchecked, then do nothing
var group=document.getElementById('checkgroup');
var os=group.getElementsByTagName('input');
for (var i=0;i<os.length;i++){
if (os[i].checked&&os[i]!=d) os[i].checked=false;
}
}
Now you can call this function in each checkbox
<div id="checkgroup">
<input id="abcd" onclick="checkone(this);">
<input id="xyz" onclick="checkone(this);">
...
</div>
Note how you don't even need to bother with the name, because the object passes in itself.

Categories