Back Button Handle A Dynamic Form - javascript

I have a form with an array of text fields. The user (through javascript) can add an arbitrary number of text fields to the form. After submitting the form and pressing the back button the form displays only with the fields that were on the original form when it was first rendered (any added text fields are lost). What is the best way to allow the back button to render the form in the state when the user submitted it? Any ideas are welcome, some things I've tried are:
Put the form data in a cookie (this
doesn't work great for a couple
reasons but the biggest killer for me
is that cookies are limited to 4K in
size)
Put the form data in a session
Submit the form via AJAX and then manage the history
Thanks for the help. I've posted a test form on my website at http://fishtale.org/formtest/f1.php. Also here is a simple form exhibiting the behavior I mentioned:
<form action="f2.php" method="post">
<input type="text" name="text[]" id="text1"/>
<input type="submit" name="saveaction" value="submit form" />
</form>
Add Form Element
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js" ></script>
<script type="text/javascript" >
$('#add_element').click(function (event) {
event.preventDefault();
$('#text1').after('<input type="text" name="text[]" />');
});
</script>
This is similar to a question I posted a while ago, Best Way For Back Button To Leave Form Data, however, this form's elements are modified by the user.

How about creating an <input type="hidden"> (with no name or outside the form so it's not submitted) in which you store an encoded list of extra fields to add and their values? While the browser won't remember newly-added fields on ‘back’, it will remember the values of hidden fields that were in the form from the start.
Here's an example that saves the extra fields on document unload and retrieves them on ready:
<input type="hidden" id="remembertexts" />
<form action="http://www.google.com/" method="get">
<div id="texts">
<input type="text" name="text[]" value="" />
</div>
<div>
<input type="submit" />
<input type="button" id="addtext" value="+" />
</div>
</form>
<script type="text/javascript">
// Add new field on button press
//
$('#addtext').click(function() {
addInput('');
});
function addInput(text) {
$('#texts input').eq(0).clone().val(text).appendTo('#texts');
};
// Store dynamic values in hidden field on leaving
//
$(window).bind('beforeunload', function() {
var vals= [];
$('#texts input').each(function() {
vals.push(encodeURIComponent(this.value));
});
$('#remembertexts').val(vals.join(';'));
});
// Retrieve dynamic values on returning to page
//
$(function() {
var extratexts= $('#remembertexts').val().split(';').slice(1);
$.each(extratexts, function() {
addInput(decodeURIComponent(this));
});
});
</script>
Notes:
You can use form.onsubmit instead of window.onbeforeunload if you only need it to remember values over a submission. onunload doesn't work as some browsers will already have stored the old form values before that event occurs.
In Firefox the position of the hidden input is important. For some reason, if you put it below the dynamically-added fields, Firefox gets confused about which input it is and fails to remember the value.
This example doesn't work in Opera. It can be made to work in Opera, but it's a pain. Opera's calling of load and unload events is inconsistent so you have to use onsubmit instead, or setting the hidden field on a polling interval, or something. Worse, when Opera remembers previous form-field values, it actually doesn't fill them in until after onload has fired! This already causes many, many form-scripting problems. You can work around that by putting a small timeout in your onload to wait until the form values have gone in if you need Opera compatibility.

I can't find a prewritten library for this, but I'm sure its been solved before. If I had to it myself I would take this approach:
Use the command pattern so that each method which modifies the page's UI by adding controls also invokes an AJAX method to push the method invoked (textual Javascript representation) onto a queue stored in the server's session.
After body onLoad completes, use an AJAX method to query the server's session for a command queue for the page the user is on. If one is retrieved, just eval each member of the queue to rebuild the page's UI in the same order the user did it.
Keep in mind with this approach you are recording not just additions of controls, but removals as well. You will require separate state holders for user input controls, like text boxes (you will also likely need server-side session with AJAX method access).

In good browsers you can have it working perfectly simply by not breaking it.
Firefox 1.5 uses in-memory caching for entire Web pages, including their JavaScript states, for a single browser session. Going backward and forward between visited pages requires no page loading and the JavaScript states are preserved. source
This is supported in Opera and WebKit too. However DOM cache is only possible in you stick to the rules:
Don't use onunload, onbeforeunload.
Don't use Cache-control: no-store or must-revalidate.
In PHP you must change session.cache_limiter from patently_ridiculous (I think they spell it nocache) to none.
session_cache_limiter('none');
Unfortunately HTTPS is also out.
If you don't force browsers to reload the page, they won't. They'll keep the DOM and its values unchanged, exactly as RFC 2616 suggests.
However, if you're looking for place to stash the data, there's incredibly clever hack – window.name can store megabytes of data. It's not sent to server, and it isn't shared between windows.
There are also Flash cookies and HTML 5 localStorage is implemented in IE8 and Safari 4.

Step 2: The script processing the form puts the values entered into an array and stores that array into a session variable (or text / db / whatever you find appropriate).
Step 1: The script which outputs the form adds a javascript (which in turn fills in the form) if that session variable is found (and it also clears the session variable).

You could make your own back button at the top of the web page and make it bigger and prettier than the standard web browser back button.
Under the hood your code could know what the previous state was and revert to it or if there was no previous state you can maybe call the browser's back function?

Block the use of the back button. When the back button is pressed, rerender the previous page for the user with the new fields included, either visibly if that makes sense, or hidden. That way the user is able to use the back button normally and you have full control over the appearance of the ''previous'' page.
In your specific use case, you just need to render the page with all the fields visible and filled in with the values that were submitted.
This is a good pattern to follow for any wizard type of process where you provide a sequence of forms for the user to fill in and they may choose to go back to a previous form.
To make it perfectly clear, I am suggesting that you use this advice on capturing the onUnload event to trigger form submission (so that you get the entered values) and to rerender the previous page that "back" would have displayed (without the values). The only alternative is to use Ajax to send the entered values every time that the user leaves a field, and then have every page check with the server via AJAX to retrieve additional values to display.
Here are some additional pages that discuss taking control over the function of the back button and using the unload event to persist forms data:
Mastering the Back Button with Javascript
Don't Let the Door Hit You
Cross Browser unload Event and the Back Button
Persisting portlet forms data in WebSphere Portal V5.1

Related

Dynamically created input element not shown after user hit Back button

I create dynamic input elements with this code:
<div id="add">Add row</div>
<script>
$(document).on('click', '#add', function() {
var number = $('div[id^="row_"]').length + 1;
$('#add').before('<div id="row_'+number+'"><label for="answer_'+number+'">Question #'+number+'</label> <input id="answer_'+number+'" name="answer_'+number+'" type="text"></div>');
});
</script>
If I send the form and hit the BACK button all other input elements show the text of the user, except the dynamically created input fields. The fields itself don't show up.
I tried to put the user's text into a hidden field via jQuery but it is also disappeared after the BACK button.
How can I show the previous state of the whole form to the user after they hit the BACK button?
SOLUTION:
If I put the user's text into a type="text" element with style="display:none", I have the text after the BACK button and I can recreate the dynamic fields with the user's text.
Thanks everyone for the suggestions, hopefully this will help others who come across this problem.
Upon POSTing the form, the browser navigates to the location (url) of your <form>'s action. When you then go back, it will reload your page and the state of all scripts is reset because the page reloaded.
If you want to keep that state, you need to send the form via AJAX. You can do that with jQuery as well (see here). Then you'll not even need to hit the back button.
I think maybe I fully understand your problem now after chatting, I hope, anyway! --
Browser Code Cache
When you download a page in Chrome or your browser, it is stored in your local browser cache. But it stores the commands, not the state, of the page. So, when you go back, the HTML is rerendered per the HTML and CSS commands, and the JS is re-executed. I found some information from v8.com, the official dev blog for Chrome...
When the JS file is requested a second time (i.e. a warm run), Chrome takes the file from the browser cache and once again gives it to V8 to compile. This time, however, the compiled code is serialized, and is attached to the cached script file as metadata.
So, you see, it recompiles and reruns the JavaScript when you hit the back button, but it does not restore the previous elements generated from the last JavaScript running, or the events that the user committed to triggered that state.
Source: V8.com: Code caching for JavaScript developers
Back-Forward Cache (BFCache)/Browser Form Cache
Don't mix this up with cached form data, which is completely different. Chrome will cache your old form data with the back button automatically, but that is not part of the code caching feature. Check out this similar question for that problem: Clear all fields in a form upon going back with browser back button
Google 2019, Feb Update: This appears to be something that should be fixed at some time within the near future. Source: Exploring a back/forward cache for Chrome.
Here is a solution:
I tried to put the user's text into a type="hidden" element via jQuery, and it has been lost, after the BACK button, however if I put the answer into a type="text" element with style="display:none", I have the user's text after the BACK button and I can recreate the dynamic fields with the user's text.
Thanks everyone for the suggestions, hopefully this will help others who come across this problem.

How can data be submitted to server without triggering a page refresh?

I have two form elements; a SELECT drop down list which serves as an input for a javascript function, and a TEXTAREA which also is an input to the same javascript function. The textarea input needs to be submitted to PHP on the server more or less simultaneously with its use by the local javascript. Ideally, both the javascript function and the $_POST submission to the server would be triggered by the same button click, but I have not been able to get this to work because use of the textarea input by the javascript function prevents it being submitted to the server and vice versa (both these actions need the same data apparently causing interference. I have successfully worked around this problem by triggering the DOM submit() to the server with an onmouseout attribute on the input button.
The problem I now have is that the page refreshes wiping away the input values when the textarea input is submitted to the server. I don't want this to happen: I want the selection from the dropdown list to remain selected, and the text that was input to the textarea to remain in the textarea to ramain after submission. I have tried with the elements inside FORM tags and outside - page gets refreshed wiping away the values either way. There are many similar questions and suggested solutions posted on StackExchange, but after spending many hours trying many of them, I have yet to find one that works for me. Basically, they fall into to categories: ones that prevent the submit such as preventDefault and return false, and ones that don't prevent the refresh or don't work at all such as various javascript/jquery submit methods. To me, such submit methods beg the question of how does the server know anything about the method used to send the submit (other than whether it is GET or POST)? Doesn't the php code on the server receive the same $_POST array regardless of the method used to send it? How would it know the difference between an html form submit, a DOM submit or a javascript/jquery submit? It is not surprising that they all trigger the same page refresh in response.
It seems like there surely should be some simple way to retain the form values after submit because surely there are many times one would want to do this.
P.S. I am no fan of jquery, I found ajax to be much easier before jquery was created. That said, at this point I would appreciate anything that works. I have almost no familiarity with jquery so please, for any responses that use jquery, please give an example of how it would be implemented in my case (where it would be placed and how it would be triggered).
PreventDefault, return false, all manner of javascript and jquery submits, removing the FORM tags, sending the return to a hidden iframe
<select id="thisselection" class="sameline"><option selected>Select this</option></select>
<script>
function sendTextarea() {
document.getElementById("pform").submit();
}
</script>
<form action="" name="pform" id="pform" method="post">
<textarea id="text" class="sendbox" name="text" cols="45" rows="10">Hello world</textarea>
</form>
<input type="button" value="Send" id="pform" onclick="myFunction.speak($('#text').val(),$('#thisselection').val());" onmouseout= "sendTextarea();"
/>
Everything works OK but I have not found a way to send the textarea input to the server without triggering a page refresh.
I wish I could simply prevent the page refresh, but lacking a means to do so, it is possible to save and restore the form values after submit (as suggested by ADyson. There is something in html5 called localStorage or sessionStorage, and there are ready-made scripts that make it easy to use, including savy.js and formsaver.js There are several of them at https://www.jqueryscript.net/tags.php?/localStorage/ savy.js works for my purpose

Issues with "Recurly.js" form generation and GWT / AJAX

I am building a user interface for Recurly account management in a GWT application.
This means there is no actual browser navigation happening between "pages" in the app, and client DOM state is maintained in memory until the user actually refreshes their browser.
In other words, when the user leaves the billing information "page" - the input elements still exist in memory and are simply detached from the visible area of the user interface.
Unfortunately, the fields that are being provided by "Recurly.js" are somewhat problematic here as they seem to be designed for use in a conventional framework where the user submits the form and then is redirected to a new page.
While they work perfectly for an initial submission of updated billing information (i.e. the first time the "form" is instantiated), if someone re-visits the screen multiple times without refreshing their browser, the fields do not accept input.
I have tried the following:
clearing the inner HTML of the wrapping recurly div elements (into
which the provided fields are drawn by Recurly.JS) and calling
configure() again
Not calling configure() after the first time it is called (in this case, subsequent visits to the page result in unresponsive inputs)
Is there a way I can tell Recurly.js to reset itself so that the provided fields can be redrawn? Or (and preferably) is there a way to configure recurly to use my own "input" fields for number, date, month, and cvv instead of those that are rendered into divs by Recurly.js?
Thank you
Edit:
I have discovered that Recurly.js is setting the visibility of their provided billing inputs to "visibility:hidden" after the form is accessed after being instantiated. If I can't force recurly to redraw the inputs, then I need to prevent this from happening some how...
I discovered a "readyState" variable in Recurly.js.
If I set that back to "0" before calling "reconfigure", the fields correctly reconfigure themselves.
This seems to fix my issue.

Getting field values from another form to perform validation on the web in lotus notes

I have a radio button field on a lotus notes form (say main form). If it is selected as "Yes", a link gets unhidden. On click of this link, another form opens up. If in the radio button, "Yes" is selected in the main form, the contents in the second form must be filled. So I need to write a validation if the radio button field is "yes" and the field inside the second form is not filled, then it should show a popup asking to fill the field in the second form. How do I get the value of the field in the second form on the main form for me to perform the validation?
You literally cannot do what you've described you want to do. The second (pop-up) form and its content no longer exist in the client (browser) context when you want to do validation on the first form's data. There are three possible ways to tackle the problem but only two of them are actually practical.
Let's dispense with the impractical method first. That would have the pop-up form write something back to its parent/opener, either as a JavaScript variable or as DOM content (a field outside of the Domino form or hidden element or some such) or, perhaps as a cookie value. Setting up the opener relationship reliably can be a problem cross-browser, but it is doable. The problem is that no matter how you do this, you have no guarantee that the value will be there when you need it (or expect it) except when the parent form is initially filled out. If the document is ever edited, you have no way of knowing whether or not the user has filled in the data on the pop-up. Anything you may have written to JS variables or the DOM during the initial session with the form only exist during the initial session. Cookies aren't permanent; they can be cleared by the user even if you try to give them eternal life via the expires value. No matter how you do it, you'd be telling anybody who has previously filled out the data you want that they need to fill it out again.
The second method would be to make a call back to the server to see if the pop-up form has been submitted and turned into a Notes document. That doesn't scale at all; even if everything is happening on a single server, there's no way to guarantee that the document you are looking for will have been written and indexed by the time you need it, and there is a time factor involved. If the user has already seen the validation nagging once, does what you tell them to do, and then gets the nag again, you're not making any friends.
The third method is to do everything you need to do on one form. (You can use CSS to do the pop-up if you're married to the pop-up idea.) And, you know, it really doesn't matter at all whether or not you would prefer to do it another way, it's the only way that will be reliable and make happy users. Yes, it will mean a little bit of extra work on your part. You're a developer - that's what you do for a living. You can even keep the structure of the existing application intact; WQO and WQS agents mean that you can glue documents together before sending them to your user, and pull them apart again before you save them. This is the only method that is guaranteed to be fast enough and reliable enough to be usable on the web.

How to trigger saved password autofill in browsers?

I have a web application written in pure JavaScript (no pre-generated HTML except for the document which loads all the JS files).
This app contains a login form which is created dynamically when the document.ready event event is triggered. I trick the browser into displaying the "Remember password?" dialog by posting the login form into a hidden iframe before logging in using ajax (in Firefox the password appears on the saved password list, so this part obviously works) but saved passwords never get filled in after the login screen is loaded again at a later time. The same thing happens in Firefox and Safari.
Is there something I can do or some function I can call to trigger autofill?
UPDATE: autofill works in Safari on initial page load, but not when user logs out and the login form is recreated without a page reload. In Firefox it never works.
In addition to wrap the form elements in a form element, make sure all the input (and maybe even the form) has unique name and id attributes that doesn't change. The name should probably be something descriptive like password for the password etc., not really sure to which degree browsers use this info, but "autosuggest"/"magic wand" features may work better if you use a standard name.
And of course you should make sure you're not setting any autosuggest/autofill attributes to false (the js framework might do so for some reason, if you're using any).
A third possibility is that some browsers maybe does autofill before your script loads and writes the form to the page, try making a static html version of the form and see if that works.
The easiest solution (if static forms work) is to force a page reload on logout (you probably do want to discard the "state" of the running javascript after log-out anyway, so refreshing is really a Good Thing)
Some browsers require "form" tag to enable local username & password storage. Had the same problem in AJAX application, and in my case I just added
<form>...</form>
tags.
Can't you use a similar trick - use an iframe to load the form and then when the login form is submitted you, in reality, submit the iframe form , which will contain the saved password.
To get the 'password' to appear in the visible form I think you could measure the length of the password in the iframe form and then fill the visible form's password field with the same number of *.

Categories