How do I manipulate a JavaScript array based on what elements I have in a container, and what order they are in?
See: http://www.mobafire.com/league-of-legends/item-purchase-planner
Clicking an item will move it into the "Item Sandbox", which generates or manipulates the "item" array (seen in the URL/permalink). Re-sorting any of the items inside the sandbox (debugging) reveals that the array is generated from the elements inside that container.
Edit: I guess I should explain my intentions? I'm currently working on a similar system, but was using array IDs on elements to manipulate the array. However, when I removed an element (and its value in the array) the other array IDs would no longer be accurate, and produce undesirable results. The array may contain duplicates, so I cannot use the values themselves.
Another option you have is to create an empty div for the sandbox, and every time you add an item to the sandbox, you create a new element and append to that empty div. Hence, make it visible and then you can generate an array from the children elements found the that sandbox div. In the meanwhile, you can decide whether or not to make invisible on the right div (source of the children elements)
As for the order of display, it depends on whether you are prepending or appending children elements. To be honest, I would suggest you to review some basic JavaScript and rephrase your questions
Angela
The items in the list are shown and hidden by their classes.
Click on the Magic Resist button, and this is essentially what happens:
$(".tier-wrapper").not(".magic-resist").hide();
Related
For example, document.getElementsByClassName("whatever") returns a list of elements, and each element has an index (so the element x is the [3] in that list, for example).Do HTML elements save that index inside the element, somehow? Or they're 'unaware' of their position?
Example of the usage I'd do with that property:
You click an element with class "People", using event.target when onclick. So you want to know which position it has, in the 'People' list. Let's say it's event.target.classNameIndex. So once you know the index, you can do things in JavaScript.
Obviously the simple alternative I can think of this is simply picking event.target and searching it inside the getElementsByClassName list. Or simply giving IDs to all elements. But avoiding that would be nice.
Hope you understand my question. :)
No
The elements are generated either dynamically or statically and are independent from everything done with them after being displayed. There are pure javascript ways of obtaining the index of an element in a array-like structure but they will most likely depend on the use of a element.onClick function and pairing them with other elements via some sort of selector.
No, for lots of reasons.
First of all, you are doing a query on the internal DOM structure, and the DOM tree itself might change immediately after your query. Elements can be added, moved or removed.
Furthermore, two very different queries might have overlapping results. E.g. query 1 might return:
[ <div id="a">, <div id="b"> ]
While query 2 could return:
[ <div id="b">, <div id="c"> ]
(for simplicity I am representing the results as arrays)
In the above, how would the element <div id="b"> know its unique and unchanging "index", given the truly infinite amount of possible queries, not the mention the possibly variable DOM again?
I am building a small property editor in Javascript. I have a list of objects with some properties (x, y, width, height) for drawing a "<div>" inside a div. Im making so that the user can drag images/divs around.
All these objecs are in a list and then I traverse the list and render the view. Next to the view I have a small editor with the properties. I can add multiple of these elements to the editor/viewer and of different kinds (text and images, and maybe other stuff in the future).
My question is this:
How do I make it so that when editing a property (like the X-coordinate) for the "first/top" box, it updates ONLY the corresponding object in the list (NOT in the view, the view is just being rendered from the list of objects). I'm adding all these in runtime and I have no round-trip to server. I'm cool with having a save button on each item.
You could follow some naming patterns when creating elements and creating property editors. Example, when new image element button is clicked, create image element with id : el_img_01 and property editor for corresponding image element as id : el_img_prop_01.
so for all images will have (el_img_, el_img_prop_), in order to know the img element for a property editor, we can read the property editor id, and remove the 'prop' text from the id to get the associated DOM element.
Several steps here:
You need a way to identify the elements, for instance, one
id.
You have to be able to select one element and be aware of
it. You can put a select containing all the ids, for example.
Now you can select the element, you just need to load/save
the values from your form.
I have common jQuery function and two div tags. Both div tags have different names but both containing elements of identical ids now i want to use this common Jquery function for them both?
I have implemented common function but it's not working for both.
Here's link to my jsfiddle -jsfiddle.net/xS7zF/1/
In my jsfiddle there are two div tags namely example1 and example2 and both tags have elements of identical ids. Function is working fine for first div but not for second.
please help me to sort out this.
Yeah, under the hood, jQuery selection on an ID will use the Document.GetElementById() function implemented by the browser, which is really fast, but (i guess depending on the browser) will stop after it finds the first element, since ID's should be unique and no further searching is needed after the first one is found.
For instance, rename the divs with id="eb" to class="eb" and you can still target specific elements using $("#example1 .eb") and $("#example2 .eb")
UPDATE:
Using your new Fiddle I created this: http://jsfiddle.net/xS7zF/5/
I cleaned up a lot of code and hopefully you can see what I have done. I changed all elements that appear twice from id to class. Now, when you attach an event to an element using $(".classname").click(), it attaches to all the elements. In the handler function where you set HTML and do your show()/hide(), you don't target a specific element using it's ID, but you find it relative to the element that does the event. You can do this using parent(), parentsUntil(), next(), find(), etc. Check jQuery docs for all possibilities. So for instance, the change-handler attaches to all inputs with name=Assets. But instead of doing $("#b1").show(), I go to the parent of the specific input that fires using $(this).parent(). Then I find the element with a class=".b1", which it will only find the one that is next to this specific input and I set the HTML to just that element.
Since there is another input, the same actions happen when THAT input changes, but instead it finds IT's parent, and finds the element with class=".b1" that is next to IT. So both divs with input are contained since they act on elements relative to itself and not across the document.
For extra fun and to show you how flexible this way of programming is, here is a fiddle with the Javascript-code unchanged, but with the exact same question-div copied 8 times. No matter how many times you repeat this, the same code will act on as many divs as you create since everything works relative. http://jsfiddle.net/xS7zF/7/
Hopefully this helps, the rest is up to you!
ID's must be unique, you should not repeat them. You could replace id with class and in the jQuery function do (".ub").each() or manually referencing the object using eq(x). e.g. (".ub").eq(1).
You shouldn't assign same id's to different elements.
You CAN but you SHOULDN'T. Instead of giving the same id, use class
IDs must be unique, try fix this, change to classes.
You can try something like this:
$("div div:first-child")
instead of
$("#eb")
But depends of the rest of your page code. So, change to classes first and use
$(".eb")
when jQuery / javascript find the first ID it would ignore the rest, please read more about it
http://www.w3schools.com/tags/att_global_id.asp
I am building an app which features a kind of "playlist". This is represented an ng-repeated custom directive with ng-repeat = "element in playlist"
Because I want to allow a user to re-use the same element twice in the playlist, I tried using the track by $index addition.
Now, what's confusing is: when I came to remove an element from the playlist (I have a function removeElement(index) which essentially contains something like this:
$scope.removeElement = function(index){
$scope.playlist.splice(index, 1);
}
Something weird happened: the element was removed correctly from $scope.playlist, but for some reason the view didn't update properly. The last element appeared to be removed.
Furthermore, I couldn't properly re-order the elements in the array either.
When I removed track by $index this problem disappears, so I assume this is because when you remove an item from the array, if you're only looking at the indices, then it appears you've just deleted the last one.
The behaviour is odd though, because transcluded content is correctly removed -- see this plunker
EDIT: The above link has been modified to show the problem better and also show the answer I settled on.
The question has also been slightly edited, to make it clearer what I was getting at. KayakDave's answer below is still correct, but is more suited to an array of primitives (which my original plunker featured).
TL;DR: How do you put duplicate elements in an ng-repeat without sacrificing the ability to control their position, or remove elements correctly?
One of the big performance advantages of using track by is Angular doesn't touch any DOM element whose tracking expression hasn't changed. This is a huge performance improvement for long ng-repeat lists and one of the reasons for the addition of track by.
That performance optimization is at the root of what you're seeing.
When you use $index in track by you're telling ng-repeat to tie each DOM element it creates to it's position ($index) on the first run of ng-repeat.
So the element with color style red is tagged 0, orange 1, yellow 2 ... and finally indigo 5.
When you delete a color Angular looks at the indexes you told it to track and sees that you longer have an index #5 (since your list is one shorter than before). Therefore it removes the DOM element tagged 5- which has a color style of "indigo". You still have an index #2 so the element with the color yellow stays.
What makes it confusing is that, due to data binding, the text inside the DOM element does get updated. Thus when you delete "yellow" the DOM element with the color yellow gets the text "green".
In short what you're seeing is ng-repeat leaving the DOM element styled with yellow untouched because it's tracking value (2) still exists but data binding has updated the text inside that element.
In order to add multiple entries with the same color you need to add your a unique identifier to each entry that you use for the track by. One approach is to use key-value pairs for each entry where the key is your unique identifier. Like so:
$scope.colorlist = {1:'red', 2:'orange',3:'yellow',4:'green',5:'blue',6:'indigo',7:'yellow'};
Then track by the key as follows:
<color-block ng-repeat="(key, color) in colorlist track by key" color="{{color}}" ng-transclude>
{{color}}
</color-block>
And make sure to use that key for your delete select:
<option value="{{key}}" ng-repeat="(key,color) in colorlist">{{color}}</option>
Now the DOM element with the color styling yellow is tied to the key you specified for the "yellow" array element. So when you delete "yellow" the ng-repeat will remove the correct DOM element and everything works.
You can see it work here: http://plnkr.co/edit/cFaU8WIjliRjPI6LInZ0?p=preview
I'd like to add another answer to this question, because I discovered a simpler solution.
There's an important section of the documentation for ng-repeat which is easy to miss, specifically on the dupes error.
It states:
By default, collections are keyed by reference
After reading this, the solution was obvious - as I wasn't dealing with primitives (yes, the plunker is, but that was an over-simplification) I needed to copy the duplicate object and add its copy to the array. This means everything works as expected when you remove track by $index and just let the default behaviour take over.
Angular makes this especially easy because jqlite has a .copy. method.
Here's what I'm saying demonstrated in a plunker.
This is the same question as this:
Referring to a div inside a div with the same ID as another inside another
except for one thing.
The reason there are two elements with the same ID is because I'm adding rows to a table, and I'm doing that by making a hidden div with the contents of the row as a template. I make a new div, copy the innerhtml of the template to my new div, and then I just want to edit bits of it, but all the bits have the same ID as the template.
I could dynamically create the row element by element but it's a VERY complex row, and there's only a few things that need to be changed, so it's a lot easier to just copy from a template and change the few things I need to.
So how do I refer to the elements in my copy, rather than the template?
I don't want to mess up the template itself, or I'll never be able to get at the bits for a second use.
Or is there another simpler way to solve the problem?
It will probably just be easiest when manipulating the innerHtml to do a replace on the IDs for that row. Maybe something like...
var copiedRow = templateRow.innerHTML.replace(/id=/g,"$1copy")
This will make the copied divs be prefixed with "copy". You can develop this further for the case that you have multiple copies by keeping a counter and adding that count variable to the replace() call.
When you want to make a template and use it multiple times its best to make it of DOM, in a documentFragment for example.
That way it doesn't respond to document.getElementById() calls in the "live" DOM.
I made an example here: http://jsfiddle.net/PM5544/MXHRr/
id's should be unique on the page.
PM5544...
In reality, there's no use to change the ID to something unique, even though your document may not be valid.
Browsers' selector engines treat IDs pretty much the same as class names. Thus, you may use
document.querySelector('#myCopy #idToLookFor');
to get the copy.
IDs on a page are supposed to be unique, even when you clone them from a template.
If you dynamically create content on your page, then you must change the id of your newly cloned elements to something else. If you want to access all cloned elements, but not the template, you can add a class to them, so you can refer to all elements with that class:
var clonedElement = template.cloneNode(yes); // make a deep copy
clonedElement.setAttribute("id", "somethingElse"); // change the id
clonedElement.setAttribute("class",
clonedElement.getAttribute("class") + " cloned"
);
To access all cloned elements by classname, you can use the getElementsByClassName method (available in newer browsers) or look at this answer for a more in-depth solution: How to getElementByClass instead of GetElementById with Javascript?
Alternatively, if you have jQuery available, you can do this is far less lines of code:
$("#template").clone().attr("id","somethingElse")
.addClass("cloned").appendTo("#someDiv");
The class lookup is even simpler:
$(".cloned").doSomethingWithTheseElements();
Try to avoid using IDs in the child elements of the cloned structure, as all ids of the cloned element should be changed before adding the clone to the page. Instead, you can refer to the parent element using the new id and traverse the rest of the structure using classnames. Class names do not need to be unique, so you can just leave them as they are.
If you really must use ID's (or unique "name" attributes in form fields), I can strongly suggest using a framework like jQuery or Prototype to handle the DOM traversal; otherwise, it is quite a burden to resolve all the cross-browser issues. Here is an example of some changes deeper in the structure, using jQuery:
$("#template").clone().attr("id","somethingElse")
.addClass("cloned") // add a cloned class to the top element
.find("#foo").attr("id","bar").end() // find and modify a child element
.appendTo("#someDiv"); // finally, add the node to the page
Check out my ugly but functional cheese. I wrote a function that works like getelementbyid, but you give it a start node instead of the document. Works like a charm. It may be inefficient but I have great faith in the microprocessors running today's browsers' javascript engines.
function getelement(node, findid)
{
if (node)
if (node.id)
if (node.id == findid)
return node;
node = node.firstChild;
while(node)
{
var r = getelement(node, findid);
if (r != null)
return r;
node = node.nextSibling;
}
return null;
}
When you copy the row, don't you end up having a reference to it? At that point can't you change the ID?