I have an javascript array, which looks like:
var styles = new Array();
styles[0] = { ... Position: 0, ... };
styles[1] = { ... Position: 1, ... };
styles[2] = { ... Position: 2, ... };
...
styles[N] = { ... Position: N, ... };
I use this array to display a list, where each item is a div. The end result is this:
<div id="container">
<div>... item 1...</div>
<div>... item 2...</div>
<div>... item 3...</div>
</div>
Now the "container" div is also jquery sortable. That way I can drag/drop the items and change the position. Now whenever the user drags an item to a different position I update the positions back in the array by looping through the div items, which is pretty bad. It looks more or less like that:
var items = $("#container");
for (var i = 0; i < items.length; i++)
{
....
styles[i] = { ... Position: i, ... };
}
Is there a better way to achieve this?
Update 1:
I need to save the positions in the database, which is why I need change my array after the list has been changed. The list changes depending on other criterias. So I could have a list of 10 items or I could have a list of X items. It depends on which list the users selects. Now if the user changes one list and then wants to see a second list, then I need to make sure that the first list maintains the positions.
I think Drew Wills response is on the mark, and this might also help. A div is a JavaScript object, which means you can add properties to it. So you might be able to reduce the number of lines of JavaScript code and increase the code's expressiveness by sticking your "information object" right onto each div, meaning you'd no longer need your array.
Where does your array come from in the first place? I'm going to assume it is a JSON-ified version of some data you have on the server. So I'm assuming (guessing) that you have some kind of "each" or for loop that creates the divs from the array, perhaps like this:
for (var i = 0; i < styles.length; i++)
{
var newDiv = $("<div>" + style[i].item + "</div>";
$("#container").append(newDiv);
}
Assuming you have that, then you could modify it to this:
for (var i = 0; i < styles.length; i++)
{
var newDiv = $("<div>" + style[i].item + "</div>";
// KEY NEW LINE FOLLOWS:
newDiv.myCustomObject = styles[i];
$("#container").append(newDiv);
}
Now, as the divs get sorted all over the place, this "myCustomObject" goes along with them. You don't need the array anymore. If you need the "Position" property on "myCustomObject" to be updated, use the "index" property of the div, as Drew Wills said.
So, imagine that your "styles" object has a property in it called "Color". Imagine you want to show an alert on click that tells the color associated with the div they clicked on. You can now accomplish this without needing the "styles" array, because the divs now have everything you need. So you could do this:
$("#container div").click(function() {
alert("The color is " + this.myCustomObject.Color);
});
Later, when it comes time to post or send via ajax the positions, you could do this:
$("#container div").each(function(index) {
this.myCustomObject.Position = index;
}
NOTE: Also, some people would prefer to use the JQuery data() method to associate "myCustomObject" with the div. It achieves the same result and perhaps gets garbage collected more completely in some browsers.
Why is it you need to know the position?
You won't have to track the position at all if you can rely on jQuery's $.index() method.
When you need to know the position of an element, just use something like the following...
$("#container div").index(myDiv);
UPDATE 1:
I see -- the server needs to know the positions to store them in the DB. Nevertheless, I'm not crazy about the notion of having 2 representations of this data on the page: markup (divs), and JS array. I would look at changing my code to build the array (from the divs) at the last second... just before you send it to the server. That way you can loop over the divs in order and you won't have to maintain the order in parallel in a separate data structure. Keeping 2 structures in sync in messy and can be error-prone
Little disclaimer: I don't know everything about your page. I don't know anything about your situation that would make this suggestion unworkable, but I suppose there may be something out there.
Little tip: if there are data elements int he area that aren't represented in the markup somehow, consider just tagging the markup with arbitrary info.
link text
Related
I have the following scenario:
A JSON object array gets fetched with angular ajax and displayed as list with ng-repeat.
The list gets sorted by object property "index", which is set as default input value of the respective list item.
The user can change the input values.
On press of a button, the list gets redrawn, sorted by the updated input values.
Now there should be a counter, ++ whenever a list item changes position.
I so far managed to count all list items with updated values.
How do I register position changes for the other list items that change position?
For example: #3 changes position to #1, then #1 and #2 also change position.
Second, using Dragula, the list items are now draggable.
How can I resort the list after list items were dragged?
I also tired Angular Dragula without success.
here is my code on github.
Thank you for your help!
I had a similar issue. You need a way of getting the DOM indexing and updating the JavaScript object to match. I looped through the DOM elements, got their index values, found the corresponding item in the data object and then set the index value in the object to that of the DOM index value. Hope this example helps someone.
var updateIndexes = function() {
// this gets the index of any DOM element, like jQuery index()
function getIndex(el) {
for (var i = 0; el = el.previousElementSibling; i++);
return i;
}
// loop through the list DOM elements
for (var i = 0; i < dataArray.length; i++) {
var dataId = dataArray[i].id;
var domIndex = getIndex(document.getElementById(dataId));
dataArray[i].index= domIndex;
}
};
In ExtJS panel I need to set value of all items (e.g. textfield, pathfield) to blank. I don't want to set value of each individual item to blank but of whole panel in one go.
I am able to get list of items
function getAllChildren (panel) {
/*Get children of passed panel or an empty array if it doesn't have thems.*/
var children = panel.items ? panel.items.items : [];
/*For each child get their children and concatenate to result.*/
CQ.Ext.each(children, function (child) {
children = children.concat(getAllChildren(child));
});
return children;
}
but how to set to blank for whole panel? Please suggest what need to be done in this case.
Actually, it's not possible to do it with one liner - all at the same time. What your method returns is purely an array of objects. In fact if such syntax existed, it would iterate over all fields anyway.
Though clearing all fields, having the method you've proposed is very trivial to do. Just iterate over them all and call reset method. Mind some (especially custom) widgets might not handle it.
var fields = getAllChildren(panel);
CQ.Ext.each(fields, function(field) {
if (child.reset) {
child.reset();
}
});
You've got similar loop in your getAllChildren code - you might reset field at the same place.
The method is defined in Field type which is usually a supertype of each dialog widget. You can read more here.
i am using selectize.js to display a multiselect dropdown list witch zipcodes and cities. Its functioning well when searching and adding one item at a time, with the autocomplete search, but when im trying to add a batch of zipcodes from a multidimensional array, it totally frezzes the page, and takes more than 20 seconds to complete with insertion of a batch of 100 zip codes or so.
The following is my two add and remove functions, using data from my multidimensional array of zipcodes, with a parent region id. The events are fired via a dropdown of regions with the value of the first dimension in the region_relations array.
var control = new Array();
control.push($select[0].selectize);
control.push($select[1].selectize);
function addItemToselect(key_index, region){
var index;
for (index = 0; index < region_relations[region].length; ++index) {
control[key_index].addItem(region_relations[region][index]);
}
}
function removeItemToselect(key_index, region){
var index;
for (index = 0; index < region_relations[region].length; ++index) {
control[key_index].removeItem(region_relations[region][index]);
}
}
I can't seem to find a solution to this problem - i thought i could maybe use jQuery to set the original options checked and call the refreshItems() function in selectize, but it seems like selectize is actually emptying the original , and so options will only be present in this element if an item has been selected using selectize - the script itself includes a addItems() function, but it basically does the same as above, i hoped the selectize addItem had a efficient way of batch adding items, but as it seems right now, i might be out of luck, and we are talking a max of 100 items to add or remove at a time....?
Do you have any suggestions?
My problem can be demonstrated "real life" on this page: http://onlineboligsalg.dk/soeg-bolig/ , the controls are found on the left.
thanks in advance for your help
still learning some javascript here, got done other things but now the final and most important part of it.
I have two html pages - one of which uses javascript to dynamically add text-fields (and to remove them of course) (genmain.html) and the other one where the text field input should go(table.html).
So i have already created a function to retrieve the array of values.
function getElementArray(divName){
var names = document.getElementsByName("namefield");
}
The variable names is an array and it has all the values from fields.
The problem is I would like to set these values from array to the values of another div on the page. After some searching i understood that it could be done with 'id'-s but i'm not that sure and don't completely understand how.
Let's say i have a lot of div's on another page (table.html) but some of them have id="MAIN". I would like to change the value inside of the div
For example
<div id="MAIN">THIS PART I WANT TO CHANGE</div>
Javascript is not part of my school system and i've done CodeAcademy tutorials and that's the most i've got about this, I hope you guys can help with my issue.
The variable names is an array and it has all the values from fields.
function getElementArray(divName){
var names = document.getElementsByName("namefield");
}
Nope, you've only got reference to the elements here. You've not got the value yet.
You can get the values by iterating through the names Nodelist array and use names[i].value
The problem is I would like to set these values from array to the
values of another div on the page
If it's going to be in same page, then use innerHTML or textContent property of the DOM to assign the value.
document.getElementById("MAIN").textContent= names[1].value;
Just for demo purpose am using names[1] here so it will load the second input value.
Let's say i have a lot of div's on another page (table.html) but some
of them have id="MAIN". I would like to change the value inside of the
div
Once you move to another page, the javascript state will be lost. So you wont have access to names inside that page.
Either you must store the values into localStorage and retrieve in next page.
Else add the values to query string of your URL and retrive it there.
Edit: Update based on comments
Let us assume you have var names = document.getElementsByName("namefield"); so to store the values inside localStorage.
var myValues = [],
names = document.getElementsByName("namefield");
for(var i = 0; i < names.length; i++) {
myValues.push(names[i].value);
}
localStorage.myValues = JSON.stringify(myValues);
Now if your next page, Iinside window.onload event:
window.onload = function() {
var myValues = localStorage.getItem("myValues") ? JSON.parse(localStorage.getItem("myValues")) : [],
divElements = document.querySelectorAll("#MAIN");
for(var i =0; i < myValues.length; i++) {
divElements[i].textContent = myValues[i];
}
}
If you want to set or change the contents of an element, you can use the innerHTML property.
So in your case, document.getElementById("MAIN").innerHTML = "Whatever you want";
For the record, names in your example technically isn't an array, but a NodeList. See https://developer.mozilla.org/en/docs/Web/API/NodeList#Why_is_NodeList_not_an_Array.3F.
I have defined a function called Node which stores the properties of nodes in a graph data structure. The function is something like this:
function Node(){
...
this.outEdges = [];
this.inEdges = [];
...
}
where the inEdges and outEdges store elements of type Edge which is another function I have defined. During the program these arrays are filled with elements.
At some point in my code I need to reset these two arrays so I write:
nodes[i].outEdges.length = 0;
nodes[i].inEdges.length = 0;
where nodes is an array of elements of type Node and I am accessing an element in a for loop.
The problem is, after setting outEdges and inEdges to 0, I expected them to be [] in the nodes[i] property list. However, when I output nodes[i] into console, the outEdges and inEdges still have the elements in them. The stranger thing is that when I output nodes[i].outEdges to console, it prints [] , which is correct, but clicking on [ ] again opens the list of the elements! I can't really figure out why the nodes[i] variables don't change?
That happens (probably) because the browser prints out the empty array but by the time you check it, it has content again. So when you click to expand the browser shows the actual content.
As you can see the values [1,3,7] were added after the command console.log(o) but they are shown on the screen (even though the length shown is 0).
You're not supposed to set the length field. Just re-initialize them:
nodes[i].outEdges = [];
nodes[i].inEdges = [];
Edit: My bad, setting the length should work. It does work for me on Chrome at least. However, I still think it's safer and better style to re-init.
Just create a new object with the same name
nodes[i].outEdges = new Array();