Dynamically created input element not shown after user hit Back button - javascript

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.

Related

Dynamically created iframe on button press vanishes immediately on all browsers

I'm a novice with this stuff so I've probably made a simple mistake, but so far searches have not helped me.
some semi-relevant context: I've discovered that I can access my university timetable directly without having to go through the login process and then navigating Blackboard to the timetable page. I found by a random typo and Chrome's omnibox autofill that it's accessible as it's own page and I can manipulate the day range and week shown and for which student ID I want to show via data in the URL.
As much as this is probably a security flaw I'm trying to take advantage of it to make viewing my time table easier (it's painful to view on mobile with it squashed into the middle of another page), so I've made a page that takes some info from form elements (student number and week number), makes a complete URL from it and creates an iframe with that URL.
anyway; I have determined that the form input is not the problem as simply using document.write() with the iframe HTML object in it with the generated URL works perfectly fine (but has the other problems of that particular method).
Further, having the javascript simply spawn a basic URL of my own by the same method but with no variables in the address still has the problem.
How I create the iframe on button press:
var divelmnt = document.getElementById("ifgh");
var ifrm=document.createElement("iframe");
ifrm.setAttribute("id", "tableframe");
ifrm.setAttribute("width", "80%");
ifrm.setAttribute("height", "80%");
function refreshIframe() {
var iurl ='http://isittuesday.tk';
ifrm.setAttribute("src", iurl);
divelmnt.appendChild(ifrm);
<form method="POST">
<button onclick="refreshIframe();">Get timetable</button>
</form>
<br>
<div id="ifgh">
</div>
The frame exists for a moment or two once the button is pressed, but then vanishes. Completely, with no trace inside of the <div> and the console has no messages.
As a control, simply adding an <iframe> with a URL written in to the body of the HTML works perfectly fine.
Secondly, as previously stated, document.write('<iframe src=iurl></iframe>') instead of divelmnt.appendChildworks perfectly fine too, except that it clears the rest of the page due to the implicit document.open call, and I'd like to keep the rest of the stuff.
For reference, "ifgh" is "iframe goes here". It's 3am, it's the best I could do.
Also, I will add a child removal snippet along the lines of divelmnt.removeChild(ifrm) so I don't get multiple iframes once this all works
I have tested this in Chrome, IE, Edge, and an extremely old version of firefox I happened to have the installer for.
Unsure where to go from here, I've run out of ideas. Thank you, and my apologies for poor formatting and if anything is unclear, tell me and I'll do my best to fix that when I wake up
Your button element inside the form must have a type="button" attribute. Otherwise it just submits the form and reload the page. The default is type="submit".
<form method="POST">
<button type="button" onclick="refreshIframe();">Get timetable</button>
</form>
Alternatively, just remove the form if you don't have anywhere to submit to.

Using javascript to go to the next tabindex of a form AFTER a refresh

I am currently working on an already established system that I cant make very drastic changes to. I need to "autosave" a form every time one of the fields is entered/updated. To do this I bound an event to the onblur of every field
document.form.submit();
It goes to the processing page, does what needs to be done and comes back to the form and retrieves the information from mysql to display it. It does have a bit of a "flash" from the reload but this is not a problem. The real problem is the user loses their tabindex since essentially the page is reloaded. So when tab is pressed the form is submitted and reloaded and the tab starts back from the beginning instead of the next field as the user would expect it to.
Is there a solution using javascript (no jquery) that I could implement that would "remember" the field the user was in and go to the next field after the form is submitted?
thank you!!

appendChild() checkboxes: remember selections with browser back button

Thank you in advance to anyone who attempts to help me with this.
I have a form that I am adding checkboxes to via appendChild() - as selections for the user to chose from - based on a bunch of criteria.
When the user checks any of these boxes and then clicks the 'continue' button to post the selection to another page - and then clicks the back button - the checkboxes that were checked by the user - have now been forgotten by the browser (no longer checked).
If I use php to write the checkboxes or simply have static checkboxes - when the user checks any of these boxes and then clicks the 'continue' button to post the selection to another page - and then clicks the back button - the selected checkboxes are remembered (still checked)
My question is:
Why does the browser forget the selections the user made when I create the checkboxes with appendChild()
yet the same browser will remember the selections the user made when using static checkboxes
What is it about appendChild() that is not allowing the same browser to remember the checked selection?
[div id="mydiv"] here is where the checkboxes are going[div]
[script type="text/javascript"]
var newInput = document.createElement("INPUT");
newInput.id = "mycheckboxid";
newInput.name = "mycheckboxname";
newInput.type = "checkbox";
document.getElementById('mydiv').appendChild(newInput);
[/script]
The browser may "forget" dynamic changes to the DOM because different browsers use different strategies for caching web pages. When you hit the back button, the idea is that the browser can display its cached copy rather than re-request the page from the original web server.
It can accomplish this in (at least) two ways:
The browser caches the DOM itself of a page upon leaving it. Upon revisit (forward or back) dynamic changes will persist.
The browser caches only the original HTML of the page at load time (prior to any dynamic changes). This has the effect of losing those dynamic changes--further modification to the DOM with appendChild() or innerHTML is not recorded.
Note that some browsers additionally keep modified form data, and others do not. If your goal is 99+% compatibility across all browsers, then you have some work to do.
To work around this you need to persist the state somehow. You have a few options:
Save data about the modifications to the page to localstorage. Use a key that is generated randomly on first page load and then kept in the page, so that the state changes will only apply to that instance of the page. On page load, if this key already exists, read the change data out and re-apply the changes. Older browsers do not support local storage.
Do the prior thing with cookies. I don't recommend this, as it has the drawback of proliferating cookies. Cookies are sent and received in every request (including ajax ones), so you would be bloating the data being transmitted on every request. Old browsers would work fine with this.
Abandon your dynamic change model and make the changes occur through a post to the server. Then the page will contain the modified html when pulled from the browser's cache. You probably don't want this, but I thought I'd point it out for completeness' sake.
Save data about the modifications to the page via ajax behind the scenes to the server. This is not the same as actually round-tripping each change like the previous item. You still make changes dynamically, but you post an "advisement" file to the server. Then, on each page load, request any adjustment data from the server. This is similar to the first suggestion, but you are using the remote server as your storage. This makes extra net traffic occur on each page load, but the traffic can be minimal as it would be just about this page. It also makes extra net traffic occur that would not normally be sent (the advisement data). A clever system architecture, however, could use this information to persist a user's unsubmitted form data across computers and over time in a way that could be very handy (lets say your user does 199 out of a 200-question survey and then his power goes out--with this scheme he has a chance of painlessly continuing later exactly where he left off!).
Make your Continue button open a new browser window, preserving the original page intact.
Make your Continue button post the data without leaving the page, preserving it intact. You could do a simple lightbox-style overlay.
If the lightbox-style overlay will not work but you really have to display a new page and don't want it to be in a new window, then redesign your site to work similarly to gmail: where pages change only through javascript, and only through using #hash tags at the end of URLs to control behavior. This can be difficult but there are libraries out there that can accomplish it. (For some browsers one has to resort to polling to see if the hashtag has changed.) The basic idea is that when you click a link that points to the same page but has a tag on it such as About the browser will initiate a page load event, and will push a new context into the history forward/back stack, but will not actually load a new page. You then parse the updated URL for the hash code (which maps to some kind of command) and carry it out. Through careful choice of the proper hash codes for each link, you can hide and display the appropriate page dynamically through Javascript and it will appear as if the person is navigating around a real web site. The reason you do all this is that, because the page never loads, you not only can maintain state in Javascript, you can maintain your DOM state as well--you simply hide the page that was modified by the user, and when the back event occurs that means to visit that page again, you display it, and it is exactly how the user left it. Advantage: If your site requires Javascript to operate, then you are not taking a risk by using even more Javascript to accomplish it. Disadvantage: Completely changing the architecture of your site is a LOT of work and can be difficult to get working on older browsers. To get started with this see Unique URLs. You might try out the jQuery hashchange plugin. If your web site has wide distribution you will want to be sure to address search engine optimization and web usability issues. You may want to see this SO page on detecting back button hash changes.
Use the same strategy as in the prior point but instead of doing it with hashtags, use the new HTML5 history.pushState() and history.replaceState() methods--see Mozilla browser history.
If your goal is not 99% compatibility across 99% of the browsers in use, then please let us know what you are aiming at, as there may be a shortcut possible.
Update: added an option #8
Scripting pages doesn't stop at state management. It includes state management.
This means scripted state changes such as scripted page transitions(pages that internally navigate), content panes, popover menus , style changes and of course, form input and selections are all the responsibility of the scripter.
So, in answer to why .. it is because you did not manage the page state you scripted.
If you want your page to work as you seem to expect you can manage the page state changes you script yourself, use a js lib that manages page, or perhaps in your case form, state, or use the http(s) client/server state management and load up the session state, or in your case just the form state, at the server.

JS page formatting is not retained when navigating away from a page and back

I have a bit of an issue with page formatting when I navigate away, and then hit browser back to a page.
Here is an example:
I have security questions on a form in a drop down list like so:
http://i.stack.imgur.com/ib32z.jpg
When the user selects [Type in your own question] from the drop down list, I have some jquery that animates a CSS change that pushes the form down, and makes visible a hidden field for 'custom security question'. When selected, the form looks like this:
http://i.stack.imgur.com/uVPKo.jpg
Now my dilemma is when I navigate away from this page, and then navigate back using the browsers back button, my formatting gets screwed up and looks like this:
http://i.stack.imgur.com/5Xhpi.jpg
The javascript that I have written does not trigger again on the back button so the browser doesn't know to move the form back down to accomodate the change in spacing. Is there anyway I can force the document.ready to reload or clear some kind of cache?
Thanks!
EDIT: Sorry guys, I need to reupload the images to a host and repost. Sorry for the delay.
There are basically four mechanisms for persisting state on the web:
Browser-based - the browser, if you're lucky, will save answers to form fields and re-display them when it sees an INPUT with the same name; also, some browsers will preserve some state between forward<=>back navigation
Cookie-based - pretty self-explanatory; you save a cookie with the state info, and check it later to recover the state
URL-based - navigate to a different hash of your URL, with the info you want in it (eg. "?roll_down=true")
HTML5/Local Storage - Look it up if you're interested :-)
We can basically throw 1 and 4 out, because they both rely too much on browser behavior/support, and we can't reliably rely on all browsers to handle them the way we want. That leaves #2 or #3.
Cookies allow you to save more info (as much as a cookie holds, ie. about 4k). URLs allow less info, but they have the added benefit of bookmark-ability; if the user saves the URL as a bookmark (or as a link they send a friend, or whatever), the state still gets preserved.
So, take your pick of the above, decide on how to persist your "my form is rolled down" state ... and then comes the part that (I think) you're really interested in: how do you check this state and fix things when the user clicks "back"?
That part I humbly defer to another SO post, which has already answered it:
Is there a way to catch the back button event in javascript?

Back Button Handle A Dynamic Form

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

Categories