I am using ASP.Net MVC along with Jquery to create a page which contains a contact details section which will allow the user to enter different contact details:
<div id='ContactDetails'>
<div class='ContactDetailsEntry'>
<select id="venue_ContactLink_ContactDatas[0]_Type" name="venue.ContactLink.ContactDatas[0].Type">
<option>Email</option>
<option>Phone</option>
<option>Fax</option>
</select>
<input id="venue_ContactLink_ContactDatas[0]_Data" name="venue.ContactLink.ContactDatas[0].Data" type="text" value="" />
</div>
</div>
<p>
<input type="submit" name="SubmitButton" value="AddContact" id='addContact' />
</p>
Pressing the button is supposed to add a templated version of the ContactDetailsEntry classed div to the page. However I also need to ensure that the index of each id is incremented.
I have managed to do this with the following function which is triggered on the click of the button:
function addContactDetails() {
var len = $('#ContactDetails').length;
var content = "<div class='ContactDetailsEntry'>";
content += "<select id='venue_ContactLink_ContactDatas[" + len + "]_Type' name='venue.ContactLink.ContactDatas[" + len + "].Type'><option>Email</option>";
content += "<option>Phone</option>";
content += "<option>Fax</option>";
content += "</select>";
content += "<input id='venue_ContactLink_ContactDatas[" + len + "]_Data' name='venue.ContactLink.ContactDatas[" + len + "].Data' type='text' value='' />";
content += "</div>";
$('#ContactDetails').append(content);
}
This works fine, however if I change the html, I need to change it in two places.
I have considered using clone() to do this but have three problems:
EDIT: I have found answers to questions as shown below:
(is a general problem which I cannot find an answer to) how do I create a selector for the ids which include angled brackets, since jquery uses these for a attribute selector.
EDIT: Answer use \ to escape the brackets i.e. $('#id\\[0\\]')
how do I change the ids within the tree.
EDIT: I have created a function as follows:
function updateAttributes(clone, count) {
var f = clone.find('*').andSelf();
f.each(function (i) {
var s = $(this).attr("id");
if (s != null && s != "") {
s = s.replace(/([^\[]+)\[0\]/, "$1[" + count + "]");
$(this).attr("id", s);
}
});
This appears to work when called with the cloned set and the count of existing versions of that set. It is not ideal as I need to perform the same for name and for attributes. I shall continue to work on this and add an answer when I have one. I'd appreciate any further comments on how I might improve this to be generic for all tags and attributes which asp.net MVC might create.
how do I clone from a template i.e. not from an active fieldset which has data already entered, or return fields to their default values on the cloned set.
You could just name the input field the same for all entries, make the select an input combo and give that a consistent name, so revising your code:
<div id='ContactDetails'>
<div class='ContactDetailsEntry'>
<select id="venue_ContactLink_ContactDatas_Type" name="venue_ContactLink_ContactDatas_Type"><option>Email</option>
<option>Phone</option>
<option>Fax</option>
</select>
<input id="venue_ContactLink_ContactDatas_Data" name="venue_ContactLink_ContactDatas_Data" type="text" value="" />
</div>
</div>
<p>
<input type="submit" name="SubmitButton" value="AddContact" id='addContact'/>
</p>
I'd probably use the Javascript to create the first entry on page ready and then there's only 1 place to revise the HTML.
When you submit, you get two arrays name "venue_ContactLink_ContactDatas_Type" and "venue_ContactLink_ContactDatas_Data" with matching indicies for the contact pairs, i.e.
venue_ContactLink_ContactDatas_Type[0], venue_ContactLink_ContactDatas_Data[0]
venue_ContactLink_ContactDatas_Type[1], venue_ContactLink_ContactDatas_Data[1]
...
venue_ContactLink_ContactDatas_Type[*n*], venue_ContactLink_ContactDatas_Data[*n*]
Hope that's clear.
So, I have a solution which works in my case, but would need some adjustment if other element types are included, or if other attributes are set by with an index included.
I'll answer my questions in turn:
To select an element which includes square brackets in it's attributes escape the square brackets using double back slashes as follows: var clone = $("#contactFields\[0\]").clone();
& 3. Changing the ids in the tree I have implemented with the following function, where clone is the variable clone (in 1) and count is the count of cloned statements.
function updateAttributes(clone, count) {
var attribute = ['id', 'for', 'name'];
var f = clone.find('*').andSelf();
f.each(function(i){
var tag = $(this);
$.each(attribute, function(i, val){
var s = tag.attr(val);
if (s!=null&& s!="")
{
s = s.replace(/([^\[]+)\[0\]/, "$1["+count+"]");
tag.attr(val, s);
}
});
if ($(this)[0].nodeName == 'SELECT')
{ $(this).val(0);}
else
{
$(this).val("");
}
});
}
This may not be the most efficient way or the best, but it does work in my cases I have used it in. The attributes array could be extended if required, and further elements would need to be included in the defaulting action at the end, e.g. for checkboxes.
Related
I'm having a kind of problem that I think is related to how I generate my HTML... I use a JavaScript function to generate some HTML, but then it begins to misfunction... let me first paste some of my code
First, my raw HTML
<div id="effect">
<label for="s_effect">Effect: </label>
<select name="s_effect" id="s_effect" onchange="ne();">
<option value="">Select your Effect</option>
</select>
<div id="effect_description"></div>
<div id="effect_options"></div>
</div>
Then, I have a function that loads "s_effect" based on an array (that's fine and working, np).
Then, my ne() (new effect) function:
function ne(){
reset();
e = g('s_effect');
if(newEffect(e.options[e.selectedIndex].value)){
console.log("new effect created");
updateScreen();
}
}
It basically "resets" parts of the screen (error tracking and that, stuff not related with my problem), then calls to updateScreen() (note: g function is just a synonym for document.getElementById)
It goes to this function (sorry it's a lot of code...)
function updateScreen(){
if(project.effect instanceof Effect){
lock("instant");
lock("event");
showDescription();
generateOptions();
}else if(project.effect == null){
unlock("instant");
unlock("event");
}
if(project.check()){
generateButton();
}else{
npButton();
}
}
That basically, locks some part of the window, then get some HTML on calls below.
generateDescription(), the part is giving trouble, does the following:
function generateOptions(){
g('effect_options').innerHTML = '';
effectID = project.effect.calculateId();
if(effectID === false)
return false;
g('effect_options').innerHTML = project.effect.parameters.HTMLOptions;
return true;
}
It calls to an object attribute that basically dumps some HTML code:
<div>
<label for="1_color">Color: </label><input type="color" id="1_color" name="1_color" onchange="updateColor('1_color');" value="#FFFFFF">
<input type="text" name="1_color_rgb" id="1_color_rgb" onchange="updateColor('1_color_rgb');" value="#FFFFFF">
</div>
<div id="extra"></div>
<div>
<input type="button" value="Add Node" onclick="addNode();">
</div>
Finally, addNode() makes an innerHTML += [new div on "extra"] but increasing the number (2_color, 2_color_rgb, and so on).
function addNode(){
var count = ++(project.effect.parameters.colorCount);
g('extra').innerHTML +=
'<div><label for="' + count + '_color">Color: </label><input type="color" id="' + count + '_color" name="' + count + '_color" onchange="updateColor(\'' + count + '_color\');" value="#FFFFFF" />' +
'<input type="text" name="' + count + '_color_rgb" id="' + count + '_color_rgb" onchange="updateColor(\'' + count + '_color_rgb\');" value="#FFFFFF" /></div>' +
}
To this point everything is working fine, even "updateColor" works (it updates the value of each input so you can choose a color by filling any input).
The problem comes when I modify any x_color or x_color that has been added via button... It adds a new "node" but restarts the values of previous inputs.
I debugged it, and by the point is doing the adding, the innerHTML of "extra" shows all inputs with "#FFFFFF" values (initial), but on the screen, the values are right...
Any help with this may be appreciated.
PS: I'm using chrome.
Thank you!
Just to clarify, as #Forty3 answered, the problem was the fact that I was modifying the innerHTML each time, making my browser to re-render extra each time.
As he suggested, I edited my function, now looks like
function addNode(){
var count = ++(project.effect.parameters.colorCount);
var nDiv = document.createElement("div");
nDiv.innerHTML = "whatever I was putting...";
g('extra').appendChild(nDiv);
}
Now it works fine.
Thank you all for the support.
The issue, it appears, is that by reassinging the .innerHTML property of the g('extra') DOM element, you are telling the browser to re-render the element based on the HTML -- not the DOM elements and values contained within.
In other words, when you add a color, the g('extra').innerHTML gets updated with the new HTML to render an additional color selection block (i.e. 2_color). When a user then picks a new color, the browswer will update the value for the 2_color DOM element but doesn't necessarily update the innerHTML property for g('extra'). Then, when another color block is added, the innerHTML is updated once more and the browser re-renders it thereby "resetting" the values.
Instead of constructing the additional controls using HTML string, use DOM manipulation (e.g. .createElement() and .append()) in order to add your new options.
I have some input field dynamically generated inside form. I am trying to read the value of hidden input and append to to the end of text area
.<input type="hidden" id="formtype_loans_0_comment" name="formtype[loans][0][comment]" disabled="disabled" value="VAlue 1 value 123" />
<textarea id="formtype_loans_0_description" name="formtype[loans][0][description]">Text Area 1 or 1 </textarea>
<input type="hidden" id="formtype_loans_1_comment" name="formtype[loans][1][comment]" disabled="disabled" value="VAlue value 123" />
<textarea id="formtype_loans_1_description" name="formtype[loans][1][description]">test desc</textarea>
and Here is the js code, but it's not working,
var values = [];
$("input[name='formtype[loans][][description]']").each(function() {
values.push($(this).val());
});
alert(values);
Your selector "input[name='formtype[loans][][description]']" won't match any elements, because the [] in the middle will not match to the [0] or [1] (etc.) in the middle of the actual element name attributes.
For the HTML shown you could use the attribute starts with selector [name^=value]:
$('input[name^="formtype[loans]"]').each(function() {
If each textarea will always immediately follow its associated hidden input then within the .each() loop that iterates over the inputs you can say $(this).next() to get the textarea.
If the textareas might be elsewhere in the DOM then you could find them by selecting by the name attribute based on the name of the current input:
$('textarea[name="' + this.name.replace("comment", "description") + '"')
Demonstrated in context:
$('input[name^="formtype[loans]"]').each(function() {
var val = this.value
// add input's value to end of associated textarea's existing value:
$('textarea[name="' + this.name.replace("comment", "description") + '"')
.val(function(i, v) { return v + ' ' + val })
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input type="hidden" id="formtype_loans_0_comment" name="formtype[loans][0][comment]" disabled="disabled" value="Hidden value 0" />
<textarea id="formtype_loans_0_description" name="formtype[loans][0][description]">Text Area A</textarea>
<input type="hidden" id="formtype_loans_1_comment" name="formtype[loans][1][comment]" disabled="disabled" value="Hidden value 1" />
<textarea id="formtype_loans_1_description" name="formtype[loans][1][description]">Text Area B</textarea>
If you want to simply replace the textarea's current value rather than adding to the end of it then you can simplify the above to:
$('input[name^="formtype[loans]"]').each(function() {
$('textarea[name="' + this.name.replace("comment", "description") + '"')
.val(this.value)
})
var values = [],
inputs = $('input[type="hidden"]'),
textareas = $('textarea');
if (inputs.length === textareas.length) {
$.each(inputs, function(i, input) {
var val = ($(input).val()) ? $(input).val(): undefined;
if (val) {
$(textareas).eq(i).empty().val(val);
}
});
}
alert(values);
The working code above assumes a couple of things:
There will always be one textarea per hidden input.
The associated textarea will always be the next sibling after the hidden input.
Even if that is not the case, there are still various ways to resolve this challenge. But I'll break down the different parts of the code:
First, instantiate your variables. Most importantly, cache your selected HTML elements into vars: touching the DOM is expensive and negatively impacts performance (e.g. querying the DOM each time in a loop).
Next, we put a conditional test to ensure there is one textarea for each input. No need to waste time iterating through a loop looking for elements that aren't there.
Finally, iterate through each of the selected inputs confirming each of them have a value. Again, no need manipulating textarea if there is no value to insert. If there is a value in the input, insert it into the textarea that occupies the same position as the input in each of your arrays of elements.
My page shows some forms with content loaded from a database. Every row will get his own <input>. The ID of this input is equal for every row, except for the number that is attached to it, to make it unique. To make it more clear; this is how the form looks like when it loads 3 rows from the database:
<form>
<input id="Amount1" value="<?php echo $databaseValue; ?>" >
<input id="Amount2" value="<?php echo $databaseValue; ?>" >
<input id="Amount3" value="<?php echo $databaseValue; ?>" >
<input type="hidden" name="numberOfRows">
<input id="finalResult">
</form>
This is all done with the mysqli_array function. The value of numberOfRows is based on numRows function.
What I'd like to achieve is that javascript calculates the value of each existing input and put the result in finalResult, regardless the number of forms (because this may vary). If I make some changes to one of the values, the finalResult should update real-time.
What I've tried so far:
formnum contains the number of fields.
var a is created at the beginning, starting at 0. Inside it's function I create an ID, matching the fields on the page. All fields are named "Amount" + number. If this number equals the number of fields, the function will stop. This way the script won't be looking for fields that doesn't excist.
Then it gets the value of this field and adds the value to var b. var b is just created to store the value temporary, untill the function's over.
At the end the total is divided to 15. This is something extra I need. Nothing special on this line.
My code:
<script type='text/javascript'>
$(document).ready(function(){
var formnum = $("#numberOfRows").val();
var a;
var b = 0;
var formname = '#Amount';
for (a = 0; a < formnum; a++) {
var complete = formname.concat(a);
var completeContent = $(complete).val();
b = b + completeContent;
};
b = b.toFixed(2);
});
$(document).mousemove(function(event){
var formula_finalResult = b / 15;
var total_finalResult = Math.floor(formula_finalResult);
$("#finalResult").val(total_finalResult);
});
</script>
This doesn't do anything. It doesn't change the value. What's going wrong?
Make it simple:
$(function(){
var sum = 0;
// Selector to select all input whose id starts with Amount
$("input[id*='Amount']").each(function(){
sum += +$(this).val(); // Parsing as int and adding it to sum
});
$("#finalResult").val(Math.floor(sum/15)); // Storing the values
})
Assuming that all of the fields always have Amount at the beginning of their id attribute, you could use jQuery's ID selector to achieve this, without the need for any of the internal counters, etc.
I'm not entirely sure why you need to hook into the mousemove event, since the data should never change on the page (since it's being generated by PHP when the page is first loaded). The following code should achieve what you're looking for:
$(function() {
var total = 0;
$('input[id*="Amount"]').each(function() { total+= parseFloat( $(this).val() ); });
$('#finalResult').val( Math.floor( total / 15 ) );
});
Your code has an error Uncaught ReferenceError: b is not defined
see it in action here: http://jsfiddle.net/ca9vascj/
There's no reason to bring the mousemove event into this, I'm not even sure what that was needed for.
Like the above answers, here's a much simplified version. But instead of a partial ID selection, let's just give the form an ID, and then give all the needed elements inside that form a class that we can select by. We also no longer need to have the numberOfRows form element.
<form id="theForm">
<input class="formAmmount" value="5" />
<input class="formAmmount" value="10" />
<input class="formAmmount" value="27.5" />
<input class="formAmmount" value="4" />
<input class="formAmmount" value="9" />
<hr />
<input id="finalResult" />
</form>
And then our jQuery code can be reduced to this:
$(function(){
var total = 0;
$("#theForm .formAmmount").each(function(){
total += parseFloat(this.value, 10);
});
var final = Math.floor(total.toFixed(2) / 15);
$("#finalResult").val(final);
});
See it in action here: http://jsfiddle.net/ca9vascj/1/
You dont'need jQuery. The simplest way to do this is document.getElementsByTagName:
var inputs = document.getElementById('my-form').getElementsByTagName('input')
That's it. inputs.length will always get an actual count of inputs in your form. That's because getElementsByTagName() returns a NodeList object, containing a live view of the matching elements. This object is mutable; it will change in response to DOM mutations.
So if you need to get sum from all of the inputs:
function sum() {
var result = 0;
[].slice.call(inputs).forEach(function(input){
result += parseFloat(input.value)
});
return result;
}
If you are able to change the generated Html-Source I would suggest to give a new class to your InputElements.
<input id="Amount1" class="ElementToCount" value="<?php echo $databaseValue; ?>" >
Then you can calculate like that
var getSumOfElements = function() {
var Elements = $('.ElementToCount')
var sum=0
if (Elements && Elements.length>0) {
for (var i=0; i<Elements.length; i++) {
sum += Elements[i].val();
}
}
return sum
}
And to update the field you could register to the 'change'-Event
$('.ElementToCount).on('change', function() {
$('#finalResult').val(getSumOfElements());
})
So I have a dynamic form that has two columns. One has a job name and the other has an input box where the user could enter their on description of the job.
while($install_table_r = tep_db_fetch_array($install_table_query))
{
echo'
<tr class="dataTableRow">
<td class="dataTableContent">
<input type="text" id="job_name" name="job_name"
value="'.$install_table_r['name_of_job'].'" disabled />
</td>
<td class="dataTableContent">
<input type="text" name="job_desc" value="'.$install_comment['comment'].'"
onChange="insertCommentInstall(this.value,)" />
</td>
</tr>
';
}
So as you can see I have a while loop that populates this form. So it could potentially have a lot of input boxes that you can use to describe the jobs.
The issue I am having is that, when I handle this form with the AJAX I have set up. The javascript simply grabs the last job on the list and uses that as it's jobs name. So in essence it is grabbing the input box correctly it's just placing it in the wrong row.
Here is the javascript that handles this change.
var job = document.getElementsByNames("job_name").value;
var comment = document.getElementsByNames("job_desc").value;
var url = "<?php echo FILENAME_ORDERS_EDIT_AJAX; ?>?action=insert_comment_install&oID=<?php
echo $_GET['oID']; ?> &new_comment=" + value + "&jobname=" + job;
I know I should be grabbing the elements with getElementByNames but I just don't know how to pair up the comment with the proper job that it's supposed to go with. So if someone comments next to the input box for Granite Job the comment should be paired up with the job name 'Granite Job' in the database. Instead currently it will just be paired up with the last job on the list which is 'Cabinet Assembly'.
Any help would be appreciated.
First of all, you have a HTML error for the attribute id
You may not in HTML standards to give a same value for id attribute to a multiple elements.
But fortunately we can use this unique identifier to make your code works
You can edit your PHP code to some thing like this:
$counter=0;
while($install_table_r = tep_db_fetch_array($install_table_query))
{
echo'
<tr class="dataTableRow">
<td class="dataTableContent">
<input type="text" id="job_name_'.$counter.'"
value="'.$install_table_r['name_of_job'].'" disabled />
</td>
<td class="dataTableContent">
<input type="text" id="job_desc_'.$counter.'" value="'.$install_comment['comment'].'"
onChange="insertCommentInstall(this.value,'.$counter.')" />
</td>
</tr>
';
$counter++;
}
You can see we added a counter to identify our rows
Updating your Javascript code will be as follow:
var insertCommentInstall=function(value,identifier){
var job = document.getElementById("job_name_"+identifier).value;
var comment = document.getElementById("job_desc_"+identifier).value;
var url = "<?php echo FILENAME_ORDERS_EDIT_AJAX; ?>?action=insert_comment_install&oID=<?php echo $_GET['oID']; ?> &new_comment=" + value + "&jobname=" + job;
}
When you use a selector like getElementsByClassName or getElementsByTagName you are retrieving a nodelist of all elements with the specified attribute (adding a classname to your inputs would make this easier). You need to specify one particular node out of the nodelist in order to fetch it's value. In order to retrieve all values in your nodelist you need to loop through it and push the values of all its nodes into an array.
//finds all elements with classname "jobs"
var jobs = document.getElementsByClassName("jobs");
//create new array that we push all the values into
var jobValues = [];
//loop through our jobs nodelist and get the value of each input
for (var i = 0; i < jobs.length - 1; i++) {
jobValues.push(jobs[i].value);
}
jobValues; //gives you a list of all the values you pushed into the array
jobValues[5]; //gives you the value of the 6th input you looped through
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I better make a list in order to explain the steps that I would like to do:
Get the name of the last element of the html-input (has been generated via PHP)
The basic setting looks like this:
<input type='text'name='E_8' value= '123' />
<input type='text'name='E_9' value= '456' />
<input type='text'name='E_10' value= '789' />
<input type='submit' name='submit' value='Update'/>
Pass it over to a JS function
Append some additional fields (use part of the name as an id for new fields
The JS-script works fine and I am able to add fields. Also the content of the fields is being processed by the PHP script and written in a db.
Short: how do I get the last value, no matter how many fields there are?
edit: I had forgotten that there is a submit button that would appear as the last element as well ... sorry for that
There are a number of approaches, but given all other answers rely on the jQuery library (which adds an unnecessary overhead), I'll focus on showing some plain JavaScript approaches (works on recents browsers above IE8+).
var allTextInputs = document.querySelectorAll('input[type="text"]'),
lastInput = allTextInputs[allTextInputs.length - 1],
lastInputName = lastInput.name;
var allInputsTxt = document.querySelectorAll('input[type="text"]');
var lastInput = allInputsTxt[allInputsTxt.length - 1];
var lastInputName = lastInput.name;
var lastInputValue = lastInput.value;
alert('last input name : ' + lastInputName + '; last input value : ' + lastInputValue);
<input type='text'name='E_8' value= '123' />
<input type='text'name='E_9' value= '456' />
<input type='text'name='E_10' value= '789' />
<input type='submit' name='submit' value='Update'/>
If what you want is the value and not the name attribute, do this after using the same approach as above to get the name of the last <input type="text"/>:
var lastInputValue = lastInput.value;
These approaches will give the value of the last <input /> of the type="text" in the document at the point at which the code is run; to find the value of a last <input /> that's dynamically added to the document, you'll need to re-run a working approach following that element's insertion.
jQuery...
var lastInputName = $('input[type="text"]:last').attr('name');
The following jQuery code should do the trick.
var lastValue = $("input[type=text]:last").val();
Also with jQuery:
var $inputs = $("input[type=text]");
var lastValue = $inputs[$inputs.length - 1].value;
Use CSS3 selectors in combination with sizzle (jquery) to target last element
var name = $('input[name^=E_]:last')[0].name
the last value in PHP or JavaScript?
in PHP the fields are normally passed as an array, so you can get the last value using
end($array)
Even better if you name your filed like this
<input type='text'name='E[8]' value= '123' />
<input type='text'name='E[9]' value= '456' />
<input type='text'name='E[10]' value= '789' />
in JS you need to get the fields into an array and get the last.... you need something like this
var myFields = document.forms["myform"].getElementsByTagName('input'),
var lastValue = myFields[(myFields.length-1)].value;
By wrapping your code a parent element, let's says with an attribute id="inputs", here is a vanilla DOM (no jQuery) solution :
// start by finding the last-most element
var lastInput = document.getElementById('inputs').lastElementChild;
// search backward to the last 'text' element
while (lastInput && lastInput.type !== 'text') {
lastInput = lastInput.previousElementSibling;
}
// and get its value
var lastValue = lastInput ? lastInput.value : null;
The interesting part of this solution is that is create no array, so you save some JavaScript memory.
It should be ok with Firefox, Chrome and IE 9.