<form>
<input name="Team1" id="team1" type="text" value="Team1?">
<button id="dostuff" value="Go">Go</button>
</form>
<div id ="nicteamcolumn">Team: </div>
<script>
$('#dostuff').click(function(e) {
var team1 = $('input[name="Team1"]').val();
if (typeof(Storage) !== "undefined") {
if (localStorage.teamlist) {
localStorage.teamlist= localStorage.teamlist + team1;
}
else {
localStorage.teamlist = " ";
}
$('#nicteamcolumn').html("Team:" + "<br>" + localStorage.teamlist + "<br>")
}
}
</script>
Basically, when the button is clicked, I am taking the input from Team1, add it to localStorage.teamlist, and changing the html of #nicteamcolumn to localStorage.teamlist
I am getting a weird output the first time I click the button. It prints the value for team 1 a ton of times over and over. The 2nd click and on seem to be working fine as well as when I close the page and coming back and keep adding to the list.
Any help would be greatly appreciated?
It prints the value for team 1 a ton of times over and over. The 2nd click and on seem to be working
That's because the localStorage object is persistent. Even when the source code of your script changes, the localStorage values will still exist. You might be confusing localStorage with sessionStorage.
Fixing your code:
Fix the syntax error by placing a ); after the last }.
typeof is not a function, please do not make it look like one, by removing the parentheses.
The very first time, nothing will print, because you're adding a space instead of the value of Team1.
Instead of setting the property directly, it is recommended to use the .getItem and .setItem methods, to avoid mistakes as using conflicting key names.
Your current "list" is a set of a concatenated string. That's not easily maintainable. Either use an unique separator to separate your values, or use JSON.stringify and JSON.parse to store real arrays.
Demo: http://jsfiddle.net/BXSGj/
Code (using JSON):
$('#dostuff').click(function(e) {
e.preventDefault(); // Prevent the form from submitting
var team1 = $('input[name="Team1"]').val();
if (typeof Storage !== "undefined") {
// Will always show an array:
var teamlist = JSON.parse(localStorage.getItem('teamlist') || '[]');
teamlist.push(team1);
localStorage.setItem('teamlist', JSON.stringify(teamlist));
$('#nicteamcolumn').html("Team:<br>" + teamlist.join('<br>') + "<br>");
}
});
Using a separator (only showing different lines):
var SEP = '|'; // Separator example
var teamlist = (localStorage.getItem('teamlist') || '').split(SEP);
teamlist.push(team1); // Still the same
localStorage.setItem('teamlist', teamlist.join(SEP));
To remove the stored items, use the localStorage.removeItem method:
localStorage.removeItem('teamlist');
See also: Compability table for the Storage object.
Related
There have been previous questions surrounding this, however I want to know if its possible to save the whole configuration of the html select in a cookie/localStorage. By the whole configuration I mean all of the options that are currently in the select, and which option is selected.
And then load this configuration back from the cookie.
From previous questions, I currently save the select element using an onChange listener, which saves the element like:
$('#chosen-url').on("change", function () {
saveToBrowserCookies("settings_select", this);
});
function saveToBrowserCookies(key, value) {
document.cookie = key + "=" + value + "; path=/";
}
And then load the select like so (on initialization):
var savedSelect = getFromBrowserCookies("settings_select");
if (savedSelect) {
var select = document.getElementById("chosen-url");
select.value = savedSelect;
}
function getFromBrowserCookies(key) {
var cookies = {}
var all = document.cookie;
var value = null;
if (all === "") { return cookies }
else {
var list = all.split("; ");
for (var i = 0; i < list.length; i++) {
var cookie = list[i];
var p = cookie.indexOf("=");
var name = cookie.substring(0, p);
if (name == key) {
value = cookie.substring(p + 1);
break;
}
}
}
return value;
}
However this doesn't work.
You can save anything in storage or in cookie, as long as it is a string. So in your case I'd recommend:
Get all your select options and currently selected value and create
an object with structure that would be easy to understand.
Stringify this object to JSON form with JSON.stringify.
Save json string in cookie or in LS.
After reload, get saved json, read it with JSON.parse and take all needed information from the object.
Recreate select element and set its chosen value.
I think that controlling select input per se is outside of the scope of this question, especially because we don't know if you are programming in pure js or in some kind of framework. But there are many informations on how to control select inputs on the web, e.g. here you can find decent answers on "how to set select value programatically".
Edit: you edited your question, so I will also add something to my response. You are trying to save whole "this" as cookie. I am not a big fan of jquery, but I guess that "this" is not necessarily the selected value. It may be a whole DOM object or change event maybe. Try logging out "this" and see what it is. You need to save string in a cookie.
Also, check in developer tools if the cookie was saved.
Last but not least, using break in a for loop is questionable. You can try using list.find instead.
Say I have the following element:
<div class='selector' data-object='{"primary_key":123, "foreign_key":456}'></div>
If I run the following, I can see the object in the console.
console.log($('.selector').data('object'));
I can even access data like any other object.
console.log($('selector').data('object').primary_key); //returns 123
Is there a way to select this element based on data in this attribute? The following does not work.
$('.selector[data-object.foreign_key=456]');
I can loop over all instances of the selector
var foreign_key = 456;
$('.selector').each(function () {
if ($(this).data('object').foreign_key == foreign_key) {
// do something
}
});
but this seems inefficient. Is there a better way to do this? Is this loop actually slower than using a selector?
You can try the contains selector:
var key_var = 456;
$(".selector[data-object*='foreign_key:" + key_var + "']");
I think that you may gain a little speed here over the loop in your example because in your example jQuery is JSON parsing the value of the attribute. In this case it's most likely using the JS native string.indexOf(). The potential downside here would be that formatting will be very important. If you end up with an extra space character between the colon and the key value, the *= will break.
Another potential downside is that the above will also match the following:
<div class='selector' data-object="{primary_key:123, foreign_key:4562}"></div>
The key is clearly different but will still match the pattern. You can include the closing bracket } in the matching string:
$(".selector[data-object*='foreign_key:" + key_var + "}']");
But then again, formatting becomes a key issue. A hybrid approach could be taken:
var results = $(".selector[data-object*='" + foreign_key + "']").filter(function () {
return ($(this).data('object').foreign_key == foreign_key)
});
This will narrow the result to only elements that have the number sequence then make sure it is the exact value with the filter.
With a "contains" attribute selector.
$('selector[data-object*="foreign_key:456"]')
Imagine I have a loaded HTML page which has been already affected by javascript adding/deleting dynamic elements or new classes/attributes/id to elements while initializing(e.g: original source code [html] tag has no classes, after javascript loads [html] tag has class="no-responsive full-with"). Imagine after that I add/amend some id values manually (through my app). And imagine I need to be able to save in database the original source code (without any amends) but with the id attributes I added manually.
Basically I need to add a given id attribute to an element within the source code of an HTML, loaded through PHP.
Do you guys have any idea of how to do such a thing?
There's no simple solution here. The exact nature of the complex solution will be determined by your full set of requirements.
Updated Concept
You've said that in addition to changing things, you'll also be adding elements and removing them. So you can't relate the changed elements to the originals purely structurally (e.g., by child index), since those may change.
So here's how I'd probably approach it:
Immediately after the page is loaded, before any modifications are made, give every element in the a unique identifier. This is really easy with jQuery (and not particularly hard without it):
var uniqueId = 0;
$("*").attr("data-uid", function() {
return ++uniqueId;
});
Now every element on the page has a unique identifier. Next, copy the DOM and get a jQuery wrapper for it:
var clone = $("html").clone();
Now you have a reliable way to relate elements in the DOM with their original versions (our clones), via the unique IDs. Allow the user to make changes.
When you're ready to find out what changes were made, you do this:
// Look for changes
clone.find("*").addBack().each(function() {
// Get this clone's unique identifier
var uid = $(this).attr("data-uid");
// Get the real element corresponding to it, if it's
// still there
var elm = $("[data-uid=" + uid + "]")[0];
// Look for changes
if (!elm) {
// This element was removed
}
else {
if (elm.id !== this.id) {
// This element's id changed
}
if (elm.className !== this.className) {
// This element's className changed
}
// ...and so on...
}
});
That will tell you about removed and changed elements. If you also want to find added elements, just do this:
var added = $(":not([data-uid])");
...since they won't have the attribute.
You can use the information in clone to reconstruct the original DOM's string:
clone.find("[data-uid]").addBack().removeAttr("data-uid");
var stringToSend = clone[0].outerHTML;
(outerHTML is supported by any vaguely modern browser, the latest to add it was Firefox in v11.)
...and of course the information above to record changes.
Live proof of concept
HTML:
<p class="content">Some content</p>
<p class="content">Some further content</p>
<p>Final content</p>
<input type="button" id="makeChange" value="Make Change">
<input type="button" id="seeResults" value="See Results">
JavaScript:
// Probably unnecessary, but I wanted a scoping
// function anyway, so we'll give the parser time
// to completely finish up.
setTimeout(function() {
// Assign unique identifer to every element
var uniqueId = 0;
$("*").attr("data-uid", function() {
return ++uniqueId;
});
// Clone the whole thing, get a jQuery object for it
var clone = $("html").clone();
// Allow changes
$("#makeChange").click(function() {
this.disabled = true;
$("p:eq(1)").attr("id", "p1");
$("p:eq(2)").addClass("foo");
alert("Change made, set an id on one element and added a class to another");
});
// See results
$("#seeResults").click(function() {
this.disabled = true;
// Look for changes
clone.find("*").addBack().each(function() {
// Get this clone's unique identifier
var uid = $(this).attr("data-uid");
// Get the real element corresponding to it, if it's
// still there
var elm = $("[data-uid=" + uid + "]")[0];
// Look for changes
if (!elm) {
display("Element with uid " + uid + ": Was removed");
}
else {
if (elm.id !== this.id) {
display("Element with uid " + uid + ": <code>id</code> changed, now '" + elm.id + "', was '" + this.id + "'");
}
if (elm.className !== this.className) {
display("Element with uid " + uid + ": <code>className</code> changed, now '" + elm.className + "', was '" + this.className + "'");
}
}
});
});
function display(msg) {
$("<p>").html(String(msg)).appendTo(document.body);
}
}, 0);
Earlier Answer
Assuming the server gives you the same text for the page every time it's asked, you can get the unaltered text client-side via ajax. That leaves us with the question of how to apply the id attributes to it.
If you need the original contents but not necessarily identical source (e.g., it's okay if tag names change case [div might become DIV], or attributes gain/lose quotes around them), you could use the source from the server (retrieved via ajax) to populate a document fragment, and apply the id values to the fragment at the same time you apply them to the main document. Then send the source of the fragment to the server.
Populating a fragment with the full HTML from your server is not quite as easy as it should be. Assuming html doesn't have any classes or anything on it, then:
var frag, html, prefix, suffix;
frag = document.createDocumentFragment();
html = document.createElement("html");
frag.appendChild(html);
prefix = stringFromServer..match(/(^.*<html[^>]*>)/);
prefix = prefix ? prefix[1] : "<!doctype html><html>";
suffix = stringFromServer.match(/(<\/html>\s*$)/);
suffix = suffix ? suffix[1] : "</html>";
html.innerHTML = stringFromServer.replace(/^.*<html[^>]*>/, '').replace(/<\/html>\s*$/, '');
There, we take the server's string, grab the outermost HTML parts (or use defaults), and then assign the inner HTML to an html element inside a fragment (although the more I think about it, the less I see the need for a fragment at all — you can probably just drop the fragment part). (Side Note: The part of the regular expressions above that identifies the start tag for the html element, <html[^>]*>, is one of those "good enough" things. It isn't perfect, and in particular will fail if you have a > inside a quoted attribute value, like this: <html data-foo="I have a > in me">, which is perfectly valid. Working around that requires much harder parsing, so I've assumed above that you don't do it, as it's fairly unusual.)
Then you can find elements within it via html.querySelector and html.querySelectorAll in order to apply your id attributes to them. Forming the relevant selectors will be great fun, probably a lot of positional stuff.
When you're done, getting back the HTML to send to the server looks like this:
var stringToSend = prefix + html.innerHTML + suffix;
In an application I am working on I need to get a list of the names of all applicationScope variable then I need to cycle through them and filter out the ones starting with a know string say $xyx. I thought that the applicationScope.keySet().
I'm using this code for starter:
var col = applicationScope.keySet();
var itr:java.util.Iterator = col.iterator();
if (itr.hasNext()){
var str:String = itr.next();
dBar.info(str,"Value = ");
}
if I put the variable col in a viewScope it shows a list of all the keys. but when I run the script the values displayed in the dBar info are not the keys but some other information that I'm not sure where it comes from.
I should just be able to iterat through the list of keys, am I missing something?
This code is in the before page loads event
After some poking around and experimenting I got this to work:
var col = applicationScope.keySet();
var itr:java.util.Iterator = col.iterator();
while (itr.hasNext()){
var str:Map.Entry = itr.next();
if (str.substring(0,9) == "$wfsLock_"){
//do stuff
}
}
so I'm now a happy camper.
Although your code works in SSJS, it is not correct (and that's why I don't like SSJS...).
The applicationScope is an implementation of the java.util.Map interface and the keySet() method returns a Set containing the keys in that Map. Every entry is (probably) a String (other data types like integers are actually also valid). The line
var str:Map.Entry = itr.next();
doesn't cast it to a Map.Entry: it doesn't really do anything: str remains a string.
The Map interface also has an entrySet() method that returns the entries (Map.Entry). You can use that to retrieve the key as well as the value:
var it = applicationScope.entrySet().iterator();
while (it.hasNext()) {
var entry = it.next();
print( entry.getKey() + " = " + entry.getValue() );
}
(in this code the print() line will use the toString() method of the key as well as the value to send information to the console)
I see from your code that you've installed my XPages Debug Toolbar. You can also use that to quickly check what's in the scopes and what the actual datatype is.
I figured it out using the /=on/g global value and it worked there... Thanks for the help ^_^
Any other advice is appreciated though! :D
I currently am having difficulty trying to figure out where to put the formdata.Replace to remove all =on tags after output from my form.
I have multiple check boxes, the ones that are submitted go to the next page and it displays all the inputted data. However, it also keeps the =on tag from the javascript and I can only figure how to remove one of them. I'm assuming I need to input another loop somewhere, but I am confused as to how to do that since there is an unknown number of checkboxes they can choose at one time (this output is being used on multiple pages, and there's different numbers of checkboxes per page).
This is the original script I found:
<script type="text/javascript">
<!-- HIDE FROM INCOMPATIBLE BROWSERS
if (window != top)
top.location.href=location.href
document.write("<h1>Your form has been submitted!</h1><h2>You entered the following data:</h2>");
var formData = location.search;
formData = formData.substring(1, formData.length);
while (formData.indexOf("+") != -1) {
formData = formData.replace("+", " ");
}
formData = unescape(formData);
var formArray = formData.split("&");
for (var i=0; i < formArray.length; ++i) {
document.writeln(formArray[i] + "<br />");
}
// STOP HIDING FROM INCOMPATIBLE BROWSERS -->
</script>
I added formData = formData.replace("=on", " "); in the while loop, which removes the first one but not any of the other ones. I am not sure where to put it, however.
You'll want to be careful with that code, unless you don't expect user-generated input, as you may end up replacing something that you didn't want to. There are a few JS libraries that will pull out the submitted values for you in a much safer way that you may want to research.
The issue that you're encountering is that it will only replace "=on" as many times as it replaces "+" with " ". If you add an additional while-loop, you can replace "=on" until they're all gone.
while (formData.indexOf("+") != -1) {
formData = formData.replace("+", " ");
}
while (formData.indexOf("=on") != -1) {
formData = formData.replace("=on", "");
}