I have input fields that are associated together, and need to copy value from "parent" field to "child" field when something is entered into "parent" field. In short, it should work like this jsfiddle.
<form id="form1" method="..." action="...">
<p><label>Enter something: <input name="parent1"></label><br>
<label>and see what happens: <input name="child1"></label></p>
</form>
<script>
var form = document.getElementById('form1');
form.elements.parent1.onblur = function () {
var form = this.form;
form.elements.child1.value = form.elements.parent1.value;
};
</script>
Simple. Now here's the problem: I don't know how many fields the form will have (these are created dynamically). All I know is there will always be the same number of parent/child fields and their names will be the same. So form may look like:
<input name="parent[59]"> <input name="child[59]">
<input name="parent[87]"> <input name="child[87]">
...and so on. Yes, the field names are the same, except the number (because these need to be submitted as arrays) will be different.
How do I rewrite JS code, so when user enters something into parent[59], its value gets copied to child[59], when user enters something into parent[87], its value gets copied to child[87], etc?
I couldn't even get it working with single field when names of input fields contain square brackets jsfiddle
(yes, I tried escaping brackets with \ but no luck)
You can target all elements based on the attribute, and then fetch the number from the name
document.querySelectorAll('[name^=parent]').forEach(function(elem) {
elem.addEventListener('input', function() {
var n = this.name.split('[').pop();
document.querySelector('[name="child[' + n + '"]').value = this.value;
});
});
<input name="parent[59]"> <input name="child[59]">
<br /><br />
<input name="parent[87]"> <input name="child[87]">
<br /><br />
<input name="parent[187]"> <input name="child[187]">
<br /><br />
<input name="parent[3]"> <input name="child[3]">
A very simple solution is to use an Event Delegate to listen to all input events on the DOM, then if the target (i.e. event.target) element has a name attribute which contains the string literal "parent" (check using String.indexOf()), replace that with "child" (using String.replace()) and update the element with that name attribute.
document.addEventListener('input', function(inputEvent) {
if (inputEvent.target.name.indexOf('parent') > -1) {
var childName = inputEvent.target.name.replace('parent', 'child');
document.forms[0].elements[childName].value = inputEvent.target.value;
}
});
<form id="form1">
<input name="parent[59]"> <input name="child[59]">
<br /><br />
<input name="parent[87]"> <input name="child[87]">
<br /><br />
<input name="parent[187]"> <input name="child[187]">
<br /><br />
<input name="parent[3]"> <input name="child[3]">
</form>
Compare that with the non-delegate approach (Adding an event listener to all parent inputs, which requires iterating over DOM elements with class name containing parent) in this jsperf testcase. When I ran it, the non-delegate case was 33% slower.
Comparing the two approaches through the lens of algorithmic complexity, the delegate approach is a constant time algorithm, so it is Θ( 1 )1, whereas the non-delegate approach is linear, or Θ( n )1. In other words, the worst-case for the event delegate approach is that it runs once per page load, whereas the other approach will have the lambda function run as many times as there are elements with the string literal "parent" in the name attribute.
1http://discrete.gr/complexity/
Related
I have three inputs, and I'm trying to make it so that a user can enter any number of them, but at least one, in order to do a search. The backend is built to handle it just fine parsing these from the URL, but I'm having trouble client-side.
Right now, I'm using oninvalid and oninput like so:
<input type="text" id="Input1" name="Input1" required oninvalid=
"setCustomValidity('Please enter a valid Input1')" oninput="setCustomValidity('')"/>
What I'm trying to do is, in this package of 3 inputs, set the other two inputs to not be requiredand have a setCustomValidity value of '' when a value is entered in one of the fields. I also would like it to re-establish those rules if, say, the user were to change their mind after typing into the wrong field.
The JQuery I have right now:
jQuery(function ($) {
var $inputs = $("#Input1, #Input2, #Input3")
$inputs.oninput = (function () {
// Set the required property of the other input to false if this input is not empty.
$inputs.not(this).prop('required', false)
$inputs.not(this).prop("setCustomValidity", "")
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form action="" method="get">
<input type="text" id="Input1" name="Input1" required oninvalid="setCustomValidity('Please enter a valid Input1, 2, or 3')" oninput="setCustomValidity('')"/>
<input type="text" id="Input2" name="Input2" required oninvalid="setCustomValidity('Please enter a valid Input1, 2, or 3')" oninput="setCustomValidity('')"/>
<input type="text" id="Input3" name="Input3" required oninvalid="setCustomValidity('Please enter a valid Input1, 2, or 3')" oninput="setCustomValidity('')"/>
<input type="submit" value="Submit" />
</form>
Everything seems to compile correctly in Razor Pages etc with no errors, but the required properties are not being removed or changed. If anyone could offer insight into what I'm doing wrong, it'd be splendid; I'm new to JS and JQuery in general, but I can see it's powerful stuff.
setCustomValidity isn't a property or attribute, it's a function that you call from the oninvalid attribute. So it should be:
$inputs.not(this).removeAttr("oninvalid");
And when you want to re-establish it, use:
$inputs.not(this).attr("oninvalid", "setCustomValidity('Please enter a valid Input1')");
But I'm not sure you need to do this. Once you make it not required, it should never trigger the "invalid" event. So you don't need to remove it at all.
You can also do this with jQuery event listeners. To add the handler:
$elements.on("invalid", function() {
this.setCustomValidity("Please enter a valid input");
}
To remove it:
$elements.off("invalid");
You also have a typo in the assignment of $this. It should be:
var $inputs = $("#Input1, #Input2, #Input3");
And there is no jQuery .oninput() method. So it should be:
$inputs.on("input", function() {
...
});
So I have javascript code to prepend "tag:" or "vendor:" before every search term, but I wanted to hide that from the user, so I created a hidden input field to send the code but it's not properly prepending the "tag:" and "vendor:" before every word. and instead inputs the entire string, then the search terms.
<form method="get" action="/search" id="search-home">
<button type="submit" value="search"></button>
<input type="hidden" name="type" value="product" />
<input type="hidden" name="q" class="searchtext" />
<input type="text" name="red" placeholder="Search"/>
</form>
<script>
$(document).on('submit','#search-home',function(){
var searchtext = $('.searchtext').val();
$('.searchtext').val("tag:"+searchtext+"* OR vendor:"+searchtext+"*");
});
</script>
Here's what the Url looks like with the code
http://zzz.co/search?type=product&q=tag%3A+OR+vendor%3A&red=tote#fullscreen=true&search=home
Here's what it's supposed to look like.
http://zzz.co/search?type=product&q=tag%3Atote+OR+vendor%3Atote#fullscreen=true&search=home
You're getting an empty value and inserting it here:
$(document).on('submit','#search-home',function(){
var searchtext = $('.searchtext').val(); // <- HERE
$('.searchtext').val("tag:"+searchtext+"* OR vendor:"+searchtext+"*");
});
What you should be doing is getting the user given query, which is the input you named "red".
$(document).on('submit','#search-home',function(){
var searchtext = $('input[name="red"]').val();
$('.searchtext').val("tag:"+searchtext+"* OR vendor:"+searchtext+"*");
});
With the above fix, your URL will look similar to:
http://zzz.co/search?type=product&q=q=tag%3Atote+OR+vendor%3Atote&red=tote.
I do not know where you're getting your hashbang(#) from, but I would assume it will append at the end as before.
If you want to get rid of the red=tote part, you have a few options. Emptying the value via $('input[name="red"]').val(''); will make it appear in your url as red=. If you want it gone entirely, you should use $('input[name="red"].remove();.
I would also advise having your "on" hook attached to the form, not the entire document. This is just a good practice to avoid using unnecessary resources as this hook will bubble every time a form is submitted, regardless of the selector. Instead, consider:
$('form#search-home').on('submit', 'button[type="submit"]', function() { ... };
That way it will only bubble when a submit event happens on that specific form, greatly reducing the possible instances those resources are used.
Ok, I am trying to create myself a reusable function that I can have ultimately return an object of all form elements in a given container such as a div, or a specific form. However I seem to be stuck with passing the actual container/form to my function so it can run over the elements.
currently I have:
function findAllFormElements(formElem)
{
//detect all form elemets on the page in a given form/container
formObj = {};
$(formElem ':input').each(function(key, val)
{
$(document).append(val+'<br>');
});
console.log(formObj);
}
where formElem is expected to be the containing element/form element ie:
<div class="something">
<input type="text">
<input type="text">
<input type="text">
<input type="text">
<select>
<option></option>
</select>
<textarea></textarea>
</div>
or
<form class="something">
<input type="text">
<input type="text">
<input type="text">
<input type="text">
<select>
<option></option>
</select>
<textarea></textarea>
</form>
both would be acceptable container types in the long run as some forms in this project don't actually have form tags.
grant it my current example isn't exactly portraying what I want per say as it spits things out to the console, and appends them to the document but then end result once I get how I can pass the formElem parameter correctly so I can get all input types from straight <input> tags to <select>, textarea will be an object of all the form elements where there id/name is the key for the object and there value if any is the value. Either way just trying to figure out whats the best tactic for this type of capture. so I can iterate over the elements and get this information.
Using jQuery's .serializeArray() we can easily grab those <form> elements and create an object from them...
$('form').serializeArray(); // will turn into a key/value object
Since you want certain <div> collections to be serialized just do:
//turn that div (filled with inputs) into a form element
var tempForm = $('<form />', { html: $('#yourDiv').html() });
var objResult = tempForm.serializeArray(); // now serialize
jsFiddle Demo
I wanted to pick one for consistency ( though i don't think it matters ) and went with id....via document.getElementById().
no longer use
name attributes for js access
form array access
There are multiple posts on this...but just to make life easy and not think about it any more..I'm using ids only.
Are there any issues with this choice?
I don't care so much about N5 and N6 and W3 specs.
Here are some similar posts:
Best Practice: Access form elements by HTML id or name attribute?
In case of usage of id for each form input when you have multiple forms with multiple inputs on one page you'll should care about uniqueness of identifiers for each input.
So the identifiers could became long like "my-form-user-name" and "my-other-special-form-user-name", etc.
So I would suggest to give an id to form element, retrieve the form by id and then refer to its elements by name. It's easier to create unique and readable identifiers for a few forms than for 50 fields of 5 forms with 10 fields in each.
And probably the code will be more readable.
<h4>Article form</h4>
<form id="article-form" method="post">
<label>Title:</label>
<input name="title" type="text" />
<label>Text:</label>
<textarea name="text"></textarea>
<input type="submit" name="submit" value="Comment" />
</form>
<hr />
<h4>Support form</h4>
<form id="support-form" method="post">
<label>Title:</label>
<input name="title" type="text" />
<label>Text:</label>
<textarea name="text"></textarea>
<input type="submit" name="submit" value="Submit issue" />
</form>
<script type="text/javascript">
var article = document.getElementById('article-form'),
ticket = document.getElementById('support-form');
article['title'].value = 'My Article';
article['text'].value = 'The text of my article...';
ticket['title'].value = 'I found bug at your site';
ticket['text'].value = 'Bug description...';
</script>
Fiddle
But if you're using labels like in my example and want to use attribute for in them to bind them to inputs, then you'll need have identifiers for that inputs anyway.
ID is suppose to be used for unique id's of every element on the page. so it's invalid to have more then once in the same document.
BUT... the NAME attribute can be repeated legally within the html standard. When name is used on a form input item, the values that the cgi recieves is based on name=value pairs. ID doesn't have an inherent name to it.
In all honesty.. I think your mixing apples and oranges here, as the two have very different purposes.
Short Question:
How do you link a label element to an input element without using the input element's id using jQuery and javascript?
Long Question:
I am using jQuery to clone a form with possibly more than one instance of the form being available for the user to fill in.
A label's 'for' attribute is supposed to be set to the 'id' attribute of the input element that it is for. This works when the input element has a unique id.
Because I am cloning the same input element there will be multiple input elements with the same id in the document. Therefore I'm avoiding having id attributes for input elements but I'd still like to focus on the input element when the label is clicked. I also want to avoid generating random ids for fields or setting onclick events on labels.
Edit #1
Example mark up (note no ids)
<form>
<label>First Name:</label><input type='text' name='FirstName' /><br/>
<label>Last Name:</label><input type='text' name='LastName' /><br/>
</form>
Example cloning code:
var newForm = $('form').clone();
$(newForm).find('label').each(function(){
var inputElement = $(this).next('input');
// I'd love to set the label's for attribute to an element
$(this).attr('for', inputElement);
});
$(document).append(newForm);
Edit #2
There currently are three options:
Set onclick events for labels to focus on the input field they're for. Criteria for deciding which labels are for which inputs can be the next input element or something else
Embed the input fields in the label fields (might not be possible due to designer's choices)
Generate random ids while cloning each form
Well it would be nice to see the markup, but if i can assume that the markup will look somewhat like this
<form name="f1">
<label>this is my label</label>
<input />
<label>this is my other label</label>
<input />
</form>
<form name="f2">
<label>this is my label</label>
<input />
<label>this is my other label</label>
<input />
</form>
then you could do something like this
$('form label').live('click',function(){
$(this).next('input').focus();
});
you will need to use live or delegate since you're cloning the forms on the fly i'm assuming.
The simplest solution is to move the <input> tags inside the <label> tags and forgo the for attribute altogether. Per the HTML spec, <input> tags without for attributes are implicitly associated with their contents.
Try this:
<form>
<label>First Name: <input type='text' name='FirstName' /></label><br/>
<label>Last Name: <input type='text' name='LastName' /></label><br/>
</form>
(See: http://www.w3.org/TR/html401/interact/forms.html#h-17.9.1)
You shouldn't have multiple identical ids in the page. It defeats the purpose of the id attribute and is against the W3C spec.
Regardless, jQuery's $(this) could help you in this situation. Say you gave all your the "focusable" class. Then you could do:
$('.focusable').focus( function(){
$(this).doSomething();
});
This is really an HTML question. A label can be associated wtih a form control either by its for attribute having the same value as the associated control's id attribute, or by having the control as a child of the label, e.g.
<form ...>
<label for="nameField">Name:<input id="nameField" name="nameField" ... ></label>
<label>email:<input name="emailField" ... ></label>
</form>
I suppose in jQuery you need something like:
var labelAndInput = $('<label>text<input ... ></label>');
or whatever. Note that older versions of IE (and maybe more recent ones too) the label will not be associated with the control without the for attribute (or htmlFor property), there is no other way.