I have a general question. I got a form made with django, if i use submit, but not all data is correct or when there are required fields missing, the page jumps back to default.
The values inside the form are saved but not the page lay-out.
my javascript that changes lay-out:
$(function(){
$('li.fields').slice(1).hide();
$('ul').on('click', 'li.title', function(){
$(this).next().slideToggle(200)
})
});
short said, I would like to keep the status of the (un)collapsed fields.
Could someone please point me in the right direction to achieve that (new to javascript)
If you can't use AJAX (or don't know how to use it), you can use a hidden field in your form.
I've created a fiddle to simulate that.
HTML:
<input type="hidden" id="myHiddenField" />
Updated Javascript:
$(function(){
$('li.fields').slice(1).hide();
$('ul').on('click', 'li.title', function(){
$(this).next().slideToggle(200);
var myString = "[ "; // create a string to simulate an Array
$('ul li.title').each(function() {
myString += $(this).is(":visible").toString() + ", "; // iterate your list to take the visible values
});
myString += " ]"; // finishes the Array
$("#myHiddenField").val(myString); // populate your hidden field
})
// when you get back from a submit, this hidden field will have some values
if ($("#myHiddenField").val() != "") {
var arr = eval($("#myHiddenField").val()); // turn the string into an Array
for (var i = 0; i < arr.length; i++) { // iterate the Array
if (!arr[i])
$("ul li.title").eq(i).hide(); // if the Array item is false, hide the respective li
}
}
});
Related
Question
I have a form that uses jQuery for magic. On that form is a button Add Account. That button appends fields Account and Amount and also another button Remove Account (which if you can guess, removes those two fields). This all works nicely...
On the same form there is another field Salary, which I would like to compare with the total of all the Amount fields. The problem is when I use jQuery's $.each() to iterate through the Amount fields it only recognizes those fields that were present in the DOM when the page loaded, and not the newly added fields.
How can I iterate through these appended Amount fields? (Or maybe there is a better to do this altogether?)
What I'm doing now:
$(document).ready(function(){
$('#form').on('keyup', '.amount', balanceAmountsWithSalary);
});
var balanceAmountsWithSalary = function(){
var salary = parseInt($('#salary').val(),10);
var total = 0;
$('#accounts .account').each(function(){
var amount = parseInt($(this).find('.amount').val(),10);
total += amount;
});
if (total === salary) {
$('#accounts .account').each(function(){
// Do some stuff to each input.amount located in div.account
});
} else {
$('#accounts .account').each(function(){
// Do some BAD stuff to each input.amount located in div.account
});
}
}
Thanks!
Answer
So it probably would've been more helpful to include the rest of my code at the outset as the problem was a simple error in the add account event. I mislabeled my container class adding an "s" to name of the appended items only. In any case thats for the comments! Posting an example on jsFiddle helped me find this error, so here is the thing in action in case you were wondering.
As HTML code and code of Dynamic adding inputs are not provided, I have edited an existing Fiddler to get total of dynamic added input field.
In this fiddler simple for loop is used to calculate total amount.
Here is a fiddler which might help you.
//button click get total
$('#GetTotal').click( function(event){
var tableID = "NewInvoiceTable";
GetTotalAmount(tableID);
return false;
});
//Get total
function GetTotalAmount(tableID)
{
var i = $('#' + tableID + ' tr').length;
alert("Total Rows -" + i);
var TotAmt = 0;
for(j=0;j<i;j++)
{
TotAmt += parseInt($('#TotalInline-' + j).val());
}
alert("Total Amount - " + TotAmt);
}
I have a page and I display data in a table.
In each table I have a column with a checkbox which if is checked the user can modify the specific row via Javascript.
This is done as its td encapsulates either an input or a select and I make these editable for the user.
The user modifies the row and presses save and the changes are saved. So far ok.
My problem is how do I implement a cancel?
The user could choose many row i.e. check boxes and modify them but the user could also press cancel. On cancel the original values should be displayed (and the rows become non-editable again).
But how is a cancel operation implemented in Javascript? Do we store data in some global datastructures? Which would be this in Javascript?
Ok, after the addition of informations you provided I suggest you setup the following mecanism:
function getDatas() {
var oXhr;
//get datas from database:
oXhr = new XMLHttpRequest();
oXhr.onreadystatechange = function() {
if (oXhr.readyState == 4 && (oXhr.status == 200)) {
g_oData = (new DOMParser()).parseFromString(oXhr.responseText, "text/xml");
}
}
oXhr.open("POST", "yourphpscriptthatreturnsthexmldatas.php", true);
oXhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=utf-8");
oXhr.send();
}
function populateGrid() {
//use g_oData to populate your grid, but at first totally clean the body
var mygrid = document.getElementById("mygridid");
//mygrid.innerHtml = "<table><tr><td>...</td></tr></table>";
//use the xml library to parse g_oData and fill up the table:
var xmlRows = g_oData.getElementsByTagName("TAG");
var xmlRow;
iLen = xmlRows.length;
for (var i=0;i<iLen;i++) {
xmlRow = xmlRows[i];
//use xmlRow->textContent to build each cell of your table
}
}
function revertChange() {
//on cancel, revert the changes by populating the grid.
//it will use the global xml/json object loaded directly from database, to refill everything.
populateGrid();
}
I did it myself many times to refresh some datas in a page. That's basically what you're doing except that you're not requesting anything to the database, you just refill the fields.
You can just access the original value attribute of the input to get the defaultValue. Sample implementation:
$("table").on("dblclick", "td", function(e) {
var val = $(this).html();
$(this).empty().append($("<form/>").append(
$("<input/>", {type:"text"}).attr("value", val),
// ^^^^
// set the *attribute*, as if it was present in the parsed HTML
$("<button/>", {type:"reset"}).text("Reset"),
$("<button/>", {type:"button", class:"cancel"}).text("Cancel"),
$("<button/>", {type:"submit"}).text("Submit")
));
}).on("submit", "form", function(e) {
var val = $(this).find("input:text").val();
// ^^^^^
// which is equivalent to .prop("value")
/* then do something with val, e.g. send it to server via ajax */
$(this).parent().html(val);
e.preventDefault();
}).on("click", "button.cancel", function(e) {
var $form = $(this).parent(),
$input = $form.find("input:text"),
oldval = $input.attr("value");
// ^^^^^^^^^^^^^
// or .prop("defaultValue"), but not .val()!
if (oldval == $input.val() || confirm("Do you really want to discard your changes?"))
$(this).parent().html(oldval);
e.preventDefault();
});
(Demo at jsfiddle.net)
A maybe more simple solution might be to use the dblclick-handler that creates the form as a closure and just store the original html in a local variable there.
Here is a pretty simple way:
Don't replace the cell content with the form element. Keep the value (the text) in a span element and hide it when you show the form element. Then you don't have to do anything on cancel. Just show the span again and hide or remove the form element. Only update the span when the user wants to save the value.
Here is an example. The showing and hiding is all done with CSS.
<tr>
<td>
<span>value</span>
<input type='text' value='' />
</td>
<td>
<button class="save">Save</button>
<button class="revert">Revert</button>
</td>
</tr>
JS:
var rows = document.querySelectorAll('table tr');
for(var i = 0, l = rows.length; i < l; i++) {
rows[i].addEventListener('click', function(event) {
// all value display elements in the row
var spans = this.querySelectorAll('span');
// all form elements in the row
var inputs = this.querySelectorAll('input');
// handle click on save button
if (event.target.className === 'save') {
[].forEach.call(inputs, function(input, i) {
spans[i].innerHTML = input.value;
});
this.className = '';
}
// handle click on revert button
else if (event.target.className === 'revert') {
// not much to do
this.className = '';
}
else {
// update form element values
[].forEach.call(inputs, function(input, i) {
input.value = spans[i].innerHTML;
});
this.className = 'edit';
}
});
}
DEMO
You can use the HTML5 data- attributes to implement a revert function. This way, each <input> would hold it's original value in case a revert button would be used.
Here's how it'd look:
<table>
<tr>
<td><input type='text' value='change me' data-original='change me' /></td>
<td><input type='text' value='change me2' data-original='change me2' /></td>
<td><input type='button' value='revert' onclick='revert(this)'/></td>
</tr>
<table>
And the code that reverts:
function revert(btn) {
var parentTr = btn.parentNode.parentNode;
var inputs = parentTr.getElementsByTagName('input');
for(var i = 0; i < inputs.length; i++) {
if (inputs[i].type == 'text') {
inputs[i].value = inputs[i].getAttribute('data-original');
}
}
}
The data-original attribute could be generated:
By the server-side app who serves the page (see (1) demo fiddle here); or
by a JavaScript function that is executed as soon as the DOM is ready (see (2) demo fiddle for this here).
As a side solution, you could store the original values in a map object. Here's the (3) demo for this (notice I added the id for each input, so it can be used as key to the map).
Keep in mind, though, neither solutions (2) or (3) require changing in server side code (the 3 assuming your inputs have ids). And (2) feels clearer.
About the defaultValue attribute: The defaultValue attribute can be a solution only if the value to be reverted never changes and if the fields involved are text inputs.
Firstly, changing the "default value" is rather awkward and may break something else aling the page (one would expect the browsers make the defaultValue attribute read-only, but that does not seem to be the case). Secondly, you would be limited to inputs of the text type.
Still, if none of that is a problem, the code above can be quickly adapted to use them instead of data- attributes.
i'm trying to make a live search for my mobile website, I don't want to query the database every time a user type a letter so I created a ordered list with all the names that can be searched for and i'm looping through it with jquery, problem is that I have 3300 names and it's freezing the browser when it searches through them, can anyone give me a tip about better ways to do it? here is my code:
$(document).ready(function(){
$("input#search").keyup(function(){
var filter = $(this).val(), count = 0;
var html = "";
$("ol.pacientes li").each(function(){
var nome_paciente = $(this).text();
if(nome_paciente.indexOf(filter.toUpperCase()) != -1){
html = html + " " + nome_paciente;
}
$('#pacientes_hint').html(html);
});
Use the jQuery autocomplete version. You can load an array with all your names and pass it in to autocomplete, which will work on the fly.
http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/
You could change your each to:
var text = $("ol.pacientes li:contains(\""+filter.toUpperCase()+"\")").map(function() {
return $(this).text();
}).join(' ');
$('#pacientes_hint').text(text);
Besides being shorter, the only improvement will be setting the contents of $('#pacientes_hint') only at the end, which could help.
Let me know if you need a more creative solution.
First of all, you could move #pacientes_hint outside the each function.
$(document).ready(function(){
$("input#search").keyup(function(){
var filter = $(this).val(), count = 0;
var html = "";
$("ol.pacientes li").each(function(){
var nome_paciente = $(this).text();
if(nome_paciente.indexOf(filter.toUpperCase()) != -1){
html = html + " " + nome_paciente;
} // end if
}); // end each
$('#pacientes_hint').html(html);
Then, you can define ol.pacientes as a variable before the keyup handler, so it doesn't look for it everytime and in the each function, search inside the variable:
$(document).ready(function(){
var pacientes_list = $("ol.pacientes");
var pacientes_hint = $("#pacientes_hint");
$("input#search").keyup(function(){
...
$("li", $(pacientes_list)).each(function(){ // search in the container
...
}); // end each
$(pacientes_hint).html(html);
I found this online:
var x = 0; // count for array length
$("input.placeholder").each(function(){
x++; //incrementing array length
});
var _values = new Array(x); //create array to hold default values
x = 0; // reset counter to loop through array
$("input.placeholder").each(function(){ // for each input element
x++;
var the_default = $(this).val();
var default_value = $(this).val(); // get default value.
_values[x] = default_value; // create new array item with default value
});
var current_value; // create global current_value variable
$('input.placeholder').focus(function(){
current_value = $(this).val(); // set current value
var is_default = _values.indexOf(current_value); // is current value is also default value
if(is_default > -1){ //i.e false
$(this).val(''); // clear value
}
});
$('input.placeholder').focusout(function(){
if( $(this).val() == ''){ //if it is empty...
$(this).val(current_value); //re populate with global current value
}
});
As you can see, it grabs the text within a value attribute and sets it as the default_value. It then checks the current_value against the default.
I'm running into a problem.
In this example, we have an element like:
<input type="text" class="placeholder" value="potato">
If the user focuses and unfocuses the input, it works great - removing and repopulating with "potato".
However, let's say a user enters "ioqiweoiqwe", and then unfocuses the input (fills out the rest of the form"). They then go back to our input and delete all of their text, and click on another field. The input would be re-populated with "ioqiweoiqwe" - when really, we want it to be re-populated with the default_value. How do I manage to do this?
Yours sincerely,
a jQuery nub.
Note: I set up a jsfiddle here... a bit after some comments: http://jsfiddle.net/xmhCz/
I don't really know what the problem with that code is, but it looks like it was written by someone who didn't know much JavaScript. I rewrote the functionality:
$("input.placeholder").each(function() {
var me=$(this);
var defaultValue=me.val();
me.focus(function() {
if(me.val()===defaultValue) {
me.val("");
}
});
me.blur(function() {
if(me.val()==="") {
me.val(defaultValue);
}
});
});
Test it out on JSFiddle.
HTML inputs have defaultValue
I have an address finder system whereby a user enters a postcode, if postcode is validated then an address list is returned and displayed, they then select an address line, the list dissappears and then the address line is split further into some form inputs.
The issue i am facing is when they have been through the above process then cleared the postcode form field, hit the find address button and the address list re-appears.
Event though the list and parent tr have been removed from the DOM it is still reporting it is present as length 1?
My code is as follows:
jQuery
// when postcode validated display box
var $addressList = $("div#selectAddress > ul").length;
// if address list present show the address list
if ($addressList != 0) {
$("div#selectAddress").closest("tr").removeClass("hide");
}
// address list hidden by default
// if coming back to modify details then display address inputs
var $customerAddress = $("form#detailsForm input[name*='customerAddress']");
var $addressInputs = $.cookies.get('cpqbAddressInputs');
if ($addressInputs) {
if ($addressInputs == 'visible') {
$($customerAddress).closest("tr").removeClass("hide");
}
} else {
$($customerAddress).closest("tr").addClass("hide");
}
// Need to change form action URL to call post code web service
$("input.findAddress").live('click', function(){
var $postCode = encodeURI($("input#customerPostcode").val());
if ($postCode != "") {
var $formAction = "customerAction.do?searchAddress=searchAddress&custpc=" + $postCode;
$("form#detailsForm").attr("action", $formAction);
} else {
alert($addressList);}
});
// darker highlight when li is clicked
// split address string into corresponding inputs
$("div#selectAddress ul li").live('click', function(){
$(this).removeClass("addressHover");
//$("li.addressClick").removeClass("addressClick");
$(this).addClass("addressClick");
var $splitAddress = $(this).text().split(",");
$($customerAddress).each(function(){
var $inputCount = $(this).index("form#detailsForm input[name*='customerAddress']");
$(this).val($splitAddress[$inputCount]);
});
$($customerAddress).closest("tr").removeClass("hide");
$.cookies.set('cpqbAddressInputs', 'visible');
$(this).closest("tr").fadeOut(250, function() { $(this).remove(); });
});
I think you're running into the same issue I recently ran into. If you have a variable pointing to 5 DIV's (example: var divs = $('.mydivs');) and then you call jQuery's remove() on one of the DIV's, like so: divs.eq(0).remove() you'll see that divs.size() still returns 5 items. This is because remove() operates on the DOM. However... if after calling remove() you then re-set your variable: divs = $('.mydivs'); and get the size you'll now get the correct size of the array. I've added sample code displaying this below:
// get all 5 divs
var d = $('.dv');
// remove the first div
d.eq(0).remove();
// you would expect 4 but no, it's 5
alert(d.size());
// re-set the variable
d = $('.dv');
// now we get 4
alert(d.size());