How can I test which node comes first? - javascript

I have two given nodes that are stored inside variables. Is there a simple, low resource usage, solution to find which node comes first in the document? Both nodes should be siblings but may be many nodes apart.

Try compareDocumentPosition:
function theFirst(node1, node2) {
return node1.compareDocumentPosition(node2)
& Node.DOCUMENT_POSITION_FOLLOWING ? node1 : node2;
}
Note that if the nodes are in different trees, the result may be random (but consistent). You can filter out that case with & Node.DOCUMENT_POSITION_DISCONNECTED and return e.g. undefined.

Something like this should work
function isAfter(n1, n2) {
var prev = n1.previousSibling,
res = true;
while (prev) {
if ( prev === n2 ) {
prev = res = false;
} else {
prev = prev.previousSibling;
}
}
return !!res;
}
Just iterate upwards using previousSibling (or downwards using nextSibling) from the first node, and compare against the second node to see if comes after (or before) the first one.
When there's no more siblings, previousSibling returnsnull` and the loop ends.
FIDDLE

Related

Sorting a link list

I am trying to solve the LeetCode problem 148. Sort List
Given the head of a linked list, return the list after sorting it in ascending order.
I am trying to do it in a recursive way before trying something smarter, as I am learning to handle data structures.
This is my code:
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* #param {ListNode} head
* #return {ListNode}
*/
var sortList = function(head) {
let previousNode = head
if(!head){
return head
}
let node = head.next
if(!node){
return head
}
let start = head
let previousNode1 = head
function sortList1(node, previousNode){
if(node.next == null){
return start
}
let temp = node.next;
if(traverseFromHead(node)){
start = node
}
previousNode1 = node
return sortList1(temp, node)
}
return sortList1(node, previousNode)
function traverseFromHead(node){
let myPosition = start
let inserted = false
if(start.val > node.val){
previousNode1.next = node.next
node.next = start
console.log("found in head excahange", node)
return node;
}
let myprevious2 = start
while(myPosition.next != null){
if(myPosition.val>=node.val){
console.log("before check start was", start, "with position at", myPosition.val, "for point", node.val, "my previous is", myprevious2.val)
let temp = node.next
myprevious2.next = node
node.next = myPosition
// previousNode1.next = temp
console.log("after update start is", start, "with position at", myPosition.val, "for point", node.val)
return null
}
myprevious2 = myPosition;
myPosition = myPosition.next
}
return false
}
};
I am not able to get it working correctly; it must be I am doing something wrong by logic or by concept
For instance for the linked list 4→2→3→0 the expected output would be 0→2→3→4, but my code produces 2→0.
Where is the problem in my code?
You have tried to implement insertion sort.
There are these issues that prevent it from working correctly:
The base case of the recursive function is not correct. With if(node.next == null) you are stopping too early. It could well be that this tail node should be moved elsewhere in the list, yet this node's value is not compared with anything. The stop condition really should be node == null.
previousNode1 = node is not always correctly identifying the previous node. If the call to traverseFromHead moved node to elsewhere in the list, then previousNode1 should not change, because what used to be the node before node, will now have become the node before the next node you want to process. For the same reason the second argument you pass in the recursive call is most often wrong: sortList1(temp, node).
It is a bit overwhelming to have that many variants of previousNodeXX variables. I would suggest to at least eliminate this previousNode1 and continue to work with previousNode, passing it also as argument to traverseFromHead. So call it as traverseFromHead(node, previousNode) and make sure you pass the correct second argument to sortList1. There are two cases to distinguish:
When node wasn't moved, then sortList1(temp, node) is correct, but when node was moved, it should be sortList1(temp, previousNode). You can make the distinction with a conditional operator:
sortList1(temp, previousNode.next != node ? previousNode : node)
traverseFromHead only removes the node from its current position in the if case, but forgets to do the same in the more general case. In the general case, the node is inserted, but previousNode.next is not adapted, meaning you now have two nodes whose next property point to node. There are several ways to do it right. I would suggest to perform the node-removal action in all cases before doing anyting else. You could place the code for node extraction before the if statement so that it always happens:
previousNode.next = node.next // <-- should always happen
if(start.val > node.val){
//...
I can understand why you put previousNode1.next = temp in comments inside the loop. Most often this needs to happen, but not when node didn't move! To solve this dilemma, perform a quick exit when node is already at its correct position (in comparison with previousNode). So at the top of the function do:
if (node.val >= previousNode.val) return null;
Now you can be sure that node will move.
traverseFromHead has a strange while condition. With the above corrections in place, this while condition can just be the opposite of the if condition, so that you can deal with the insertion after the loop:
while (myPosition.val < node.val)
myprevious2 = myPosition;
myPosition = myPosition.next
}
Here is your code with those corrections:
var sortList = function(head) {
let previousNode = head
if(!head){
return head
}
let node = head.next
if(!node){
return head
}
let start = head
function sortList1(node, previousNode){
if(node == null){ // Corrected base case
return start
}
let temp = node.next;
if(traverseFromHead(node, previousNode)){ // Pass the second argument
start = node
}
// Depending on whether node was moved, the node that precedes temp is different
return sortList1(temp, previousNode.next != node ? previousNode : node)
}
return sortList1(node, previousNode)
function traverseFromHead(node, previousNode){ // Second argument
if (node.val >= previousNode.val) return null; // Quick exit for trivial case
previousNode.next = node.next // Always first extract the node
if(start.val >= node.val){ // Equal is also good, so >=
node.next = start
return node;
}
let myPosition = start.next // Can start iteration at second node
let myprevious2 = start
while (myPosition.val < node.val) { // Look for the insertion spot
myprevious2 = myPosition;
myPosition = myPosition.next
}
// Now perform the re-insertion
myprevious2.next = node
node.next = myPosition
return null
}
};
Other remarks
Insertion sort is not the most efficient among sorting algorithms, and for linked lists it is quite easy to implement better performing sorting algorithms.
See for instance Merge sort on linked list
I have here adapted that solution for the LeetCode challenge (spoiler):
var sortList = function(head) {
if (!head || !head.next) return head; // Nothing to sort
// Find last node of first half
let tail = head;
for (let fast = tail.next; fast?.next; fast = fast.next.next) {
tail = tail.next;
}
// Split list into two halves
let head2 = tail.next;
tail.next = null;
// Recursively sort the two shorter lists
head = sortList(head);
head2 = sortList(head2);
// Merge the two sorted lists
if (head.val > head2.val) [head2, head] = [head, head2];
tail = head;
while (tail.next && head2) {
if (tail.next.val > head2.val) [head2, tail.next] = [tail.next, head2];
tail = tail.next;
}
tail.next ??= head2;
return head;
};
MergeSort naturally fits for linked lists.
In merge sort you consider that merging 2 size one lists is trivial (just put the higher head value after the lower one).
Extending that idea, if you have two already sorted lists, then it's easy to merge them as well. Just create a new list where you add the highest of both lists till both lists are empty.
So you can do a merge sort by creating first 2 lists of size 1. (ie. the first 2 elements of your list) Then merging them.
Then create a second list of size 2 (by merging 2 of size 1).
And continue until you have merged the entire original list into a sorted list.
Recursion
To implement this recursively first write a merge function that given two sorted lists merges them by preserving the sort order.
Then do the following to implement sort:
If your list is empty, then return the list as your result
Now merge the first element with sort(rest of the list)

Breadth first search binary search tree javascript implementation

I have the following code that implements a BST tree in JavaScript.
function Node(value) {
this.left = null;
this.right = null;
this.value = value;
}
function BinarySearchTree() {
this.root = null;
return;
}
BinarySearchTree.prototype.push = function(value) {
if (!this.root) {
this.root = new Node(value);
return;
}
var currentRoot = this.root;
var newNode = new Node(value);
while (currentRoot) {
if (value < currentRoot.value) {
if (!currentRoot.left) {
currentRoot.left = newNode;
break;
} else {
currentRoot = currentRoot.left;
}
} else {
if (!currentRoot.right) {
currentRoot.right = newNode;
break;
} else {
currentRoot = currentRoot.right;
}
}
}
}
var a = new BinarySearchTree();
a.push(27);
a.push(14);
a.push(35);
a.push(10);
a.push(19);
a.push(31);
a.push(42);
I am trying to implement a function which can do a breadth first traversal of the tree. This is what I have tried so far.
console.log(a.root.value);
traverse(a.root);
//function to traverse
function traverse(node) {
currentNode = node;
while (currentNode.left) {
displayNodes(currentNode);
parent = currentNode;
currentNode = currentNode.left;
displayNodes(currentNode);
if(parent.right!=null){
displayNodes(parent.right);
}
}
}
//function that displays the left and right node of a node
function displayNodes(node) {
if (node.left != null) {
console.log(node.left.value);
}
if (node.right != null) {
console.log(node.right.value);
}
}
I am unable to implement a function that could scale with a large number of data. I am not sure if a recursive method to traverse would be better or using a while loop. How can I implement the function? I know that the function gives unexpected behavior? What correction should I make?
You currently traverse the path from the root node to the left-most leaf.
A simple non-recursive breadth-first traversal function invoking a callback on each traversed node could look as follows:
// Breadth-first traversal:
function traverse(node, cb) {
var current = [node];
while (current.length > 0) {
var next = [];
for (var node of current) {
cb(node);
if (node.left) next.push(node.left);
if (node.right) next.push(node.right);
}
current = next;
}
}
// Example:
traverse(root, function(node) {
console.log(node.value);
});
It works by keeping an array of already discovered or traversed nodes current which initially contains just your root node. Now, you iteratively replace each node in that list with its children. In above function, the children are stored in a next array. At the end of each iteration, all nodes of the current level in current are replaced with all their children of the next deeper level in next. See also the first suggestion given by #DavidKnipe's answer.
A non-recursive approach has the advantage of not being subject to the call stack size limit. This theoretically allows you to handle larger data structures when the call stack size is limited.
If you're looking for a way to BFS using O(1) memory, I don't think there's a nice way to do it. (DFS is another matter though. Are you sure it has to be BFS?)
There are two ways I can see to do this. You could start with the array [this.root], and write a function that iterates over an array of nodes and then returns an array of children of those nodes. Then call that function on the array of children, and keep going down the tree until you get an empty array.
If memory is an issue, there's another way to do it. Instead of remembering the array of nodes at a given level, you could just remember the depth, then redo the iteration each time. So you'd have a function which takes a natural number n and iterates over the tree, but without going deeper than n, and does whatever it is you're trying to do at the nth level only; then call this function for all values of n until there are no more nodes left.
That last one might sound very wasteful, but it might not be too bad if the last few levels of the tree contain most of the nodes. It depends on your dataset and computational capabilities.

Swapping Two Divs with Javascript [duplicate]

What is the easiest way to swap the order of child nodes?
For example I want childNode[3] to be childNode[4] and vice-versa.
There is no need for cloning. You can just move one node before the other. The .insertBefore() method will take it from its current location and insert it somewhere else (thus moving it):
childNode[4].parentNode.insertBefore(childNode[4], childNode[3]);
You get the parent of the node. You then call the insertBefore method on the parent and you pass it the childNode[4] node and tell it you want it inserted before childNode[3]. That will give you the result of swapping their order so 4 will be before 3 when it's done.
Reference documentation on insertBefore.
Any node that is inserted into the DOM that is already in the DOM is first removed automatically and then inserted back so there is no need to manually remove it first.
Use .before or .after!
This is vanilla JS!
childNode[3].before(childNode[4]);
or
childNode[4].after(childNode[3]);
For more durability swapping, try:
function swap(node1, node2) {
const afterNode2 = node2.nextElementSibling;
const parent = node2.parentNode;
node1.replaceWith(node2);
parent.insertBefore(node1, afterNode2);
}
This should work, even if the parents don't match
Can I Use - 95% Jul '21
Answer by jfriend00 does not really swap elements (it "swaps" only elements which are next to each other and only under the same parent node). This is ok, since this was the question.
This example swaps elements by cloning it but regardless of their position and DOM level:
// Note: Cloned copy of element1 will be returned to get a new reference back
function exchangeElements(element1, element2)
{
var clonedElement1 = element1.cloneNode(true);
var clonedElement2 = element2.cloneNode(true);
element2.parentNode.replaceChild(clonedElement1, element2);
element1.parentNode.replaceChild(clonedElement2, element1);
return clonedElement1;
}
Edit: Added return of new reference (if you want to keep the reference, e. g. to access attribute "parentNode" (otherwise it gets lost)). Example: e1 = exchangeElements(e1, e2);
I needed a function to swap two arbitrary nodes keeping the swapped elements in the same place in the dom. For example, if a was in position 2 relative to its parent and b was in position 0 relative to its parent, b should replace position 2 of a's former parent and a should replace child 0 of b's former parent.
This is my solution which allows the swap to be in completely different parts of the dom. Note that the swap cannot be a simple three step swap. Each of the two elements need to be removed from the dom first because they may have siblings that would need updating, etc.
Solution: I put in two holder div's to hold the place of each node to keep relative sibling order. I then reinsert each of the nodes in the other's placeholder, keeping the relative position that the swapped node had before the swap. (My solution is similar to Buck's).
function swapDom(a,b)
{
var aParent = a.parentNode;
var bParent = b.parentNode;
var aHolder = document.createElement("div");
var bHolder = document.createElement("div");
aParent.replaceChild(aHolder,a);
bParent.replaceChild(bHolder,b);
aParent.replaceChild(b,aHolder);
bParent.replaceChild(a,bHolder);
}
For a real Swap of any nodes without cloneNode:
<div id="d1">D1</div>
<div id="d2">D2</div>
<div id="d3">D3</div>
With SwapNode function (using PrototypeJS):
function SwapNode(N1, N2) {
N1 = $(N1);
N2 = $(N2);
if (N1 && N2) {
var P1 = N1.parentNode;
var T1 = document.createElement("span");
P1.insertBefore(T1, N1);
var P2 = N2.parentNode;
var T2 = document.createElement("span");
P2.insertBefore(T2, N2);
P1.insertBefore(N2, T1);
P2.insertBefore(N1, T2);
P1.removeChild(T1);
P2.removeChild(T2);
}
}
SwapNode('d1', 'd2');
SwapNode('d2', 'd3');
Will produce:
<div id="d3">D3</div>
<div id="d1">D1</div>
<div id="d2">D2</div>
Use a dummy sibling as a temporary position marker and then .before (or .after).
It works for any siblings (not only adjacent) and also maintains event handlers.
function swap(a, b) {
let dummy = document.createElement("span")
a.before(dummy)
b.before(a)
dummy.replaceWith(b)
}
<div id="div1">A</div>
<div id="div2">B</div>
<p> parent<div id="div3">C</div>
</p>
<button onclick="swap(div1, div3)">swap</button>
Just like temporary variables are used to swap variables, if more sophicated methods are missing.
Best way to do this is to create a temporary node
function swapNodes(node1, node2) {
const temp = document.createComment('')
node2.replaceWith(temp)
node1.replaceWith(node2)
temp.replaceWith(node1)
}
Try this method:
Get the parent element
Store the two elements you want to swap
Store the .nextSibling of the node that is last in order
eg: [1,2,3,4] => we want to swap 3 & 2 then store nextSibling of 3, '4'.
.insertBefore(3,2);
.insertBefore(2,nextSibling);
Code Explanation
val & val2 are the 2 nodes/elements to be swapped
equiv(index) gets the present node/element in DOM at index passed as the paramter
NOTE: It will count comment & text elements so take care xD
Hopes this helps :)
function equiv(index){
return Array.prototype.slice.call( document.querySelectorAll("*"))[index];
}
function swap (val,val2){
let _key = val.key;
let _key_ = val2.key;
_key_ = _key < _key_ ? _key_+1:_key_;
let _parent_ = val2.parentElement.valueOf();
if (val.parentElement.children.length ==1)
val.parentElement.appendChild(val2);
else
val.parentElement.insertBefore(val2,val);
if (_parent_.children.length ==0)
_parent_.appendChild(val);
else{
let _sibling_ = equiv(_key_);
_parent_.insertBefore(val,_sibling_);}
}
A solution that works without cloning, given the indices of the two elements to swap:
function swapChildren(parentElement, index1, index2) {
if (index1 === index2)
return
if (index1 > index2) {
const temp = index1
index1 = index2
index2 = temp
}
const { [index1]: element1, [index2]: element2 } = parentElement.childNodes
if (index2 === index1 + 1) {
parentElement.insertBefore(element2, element1)
} else {
const reference = element2.nextSibling
parentElement.replaceChild(element2, element1)
parentElement.insertBefore(element1, reference)
}
}
You can swap a DOM element with its next sibling like that:
el.parentNode.insertBefore(el, el.nextElementSibling)
Or with its previous sibling like this:
el.parentNode.insertBefore(el, el.previousElementSibling)
And if your content is dynamic, you might want to check that el.nextElementSibling or el.previousElementSibling is not null.

How to swap DOM child nodes in JavaScript?

What is the easiest way to swap the order of child nodes?
For example I want childNode[3] to be childNode[4] and vice-versa.
There is no need for cloning. You can just move one node before the other. The .insertBefore() method will take it from its current location and insert it somewhere else (thus moving it):
childNode[4].parentNode.insertBefore(childNode[4], childNode[3]);
You get the parent of the node. You then call the insertBefore method on the parent and you pass it the childNode[4] node and tell it you want it inserted before childNode[3]. That will give you the result of swapping their order so 4 will be before 3 when it's done.
Reference documentation on insertBefore.
Any node that is inserted into the DOM that is already in the DOM is first removed automatically and then inserted back so there is no need to manually remove it first.
Use .before or .after!
This is vanilla JS!
childNode[3].before(childNode[4]);
or
childNode[4].after(childNode[3]);
For more durability swapping, try:
function swap(node1, node2) {
const afterNode2 = node2.nextElementSibling;
const parent = node2.parentNode;
node1.replaceWith(node2);
parent.insertBefore(node1, afterNode2);
}
This should work, even if the parents don't match
Can I Use - 95% Jul '21
Answer by jfriend00 does not really swap elements (it "swaps" only elements which are next to each other and only under the same parent node). This is ok, since this was the question.
This example swaps elements by cloning it but regardless of their position and DOM level:
// Note: Cloned copy of element1 will be returned to get a new reference back
function exchangeElements(element1, element2)
{
var clonedElement1 = element1.cloneNode(true);
var clonedElement2 = element2.cloneNode(true);
element2.parentNode.replaceChild(clonedElement1, element2);
element1.parentNode.replaceChild(clonedElement2, element1);
return clonedElement1;
}
Edit: Added return of new reference (if you want to keep the reference, e. g. to access attribute "parentNode" (otherwise it gets lost)). Example: e1 = exchangeElements(e1, e2);
I needed a function to swap two arbitrary nodes keeping the swapped elements in the same place in the dom. For example, if a was in position 2 relative to its parent and b was in position 0 relative to its parent, b should replace position 2 of a's former parent and a should replace child 0 of b's former parent.
This is my solution which allows the swap to be in completely different parts of the dom. Note that the swap cannot be a simple three step swap. Each of the two elements need to be removed from the dom first because they may have siblings that would need updating, etc.
Solution: I put in two holder div's to hold the place of each node to keep relative sibling order. I then reinsert each of the nodes in the other's placeholder, keeping the relative position that the swapped node had before the swap. (My solution is similar to Buck's).
function swapDom(a,b)
{
var aParent = a.parentNode;
var bParent = b.parentNode;
var aHolder = document.createElement("div");
var bHolder = document.createElement("div");
aParent.replaceChild(aHolder,a);
bParent.replaceChild(bHolder,b);
aParent.replaceChild(b,aHolder);
bParent.replaceChild(a,bHolder);
}
For a real Swap of any nodes without cloneNode:
<div id="d1">D1</div>
<div id="d2">D2</div>
<div id="d3">D3</div>
With SwapNode function (using PrototypeJS):
function SwapNode(N1, N2) {
N1 = $(N1);
N2 = $(N2);
if (N1 && N2) {
var P1 = N1.parentNode;
var T1 = document.createElement("span");
P1.insertBefore(T1, N1);
var P2 = N2.parentNode;
var T2 = document.createElement("span");
P2.insertBefore(T2, N2);
P1.insertBefore(N2, T1);
P2.insertBefore(N1, T2);
P1.removeChild(T1);
P2.removeChild(T2);
}
}
SwapNode('d1', 'd2');
SwapNode('d2', 'd3');
Will produce:
<div id="d3">D3</div>
<div id="d1">D1</div>
<div id="d2">D2</div>
Use a dummy sibling as a temporary position marker and then .before (or .after).
It works for any siblings (not only adjacent) and also maintains event handlers.
function swap(a, b) {
let dummy = document.createElement("span")
a.before(dummy)
b.before(a)
dummy.replaceWith(b)
}
<div id="div1">A</div>
<div id="div2">B</div>
<p> parent<div id="div3">C</div>
</p>
<button onclick="swap(div1, div3)">swap</button>
Just like temporary variables are used to swap variables, if more sophicated methods are missing.
Best way to do this is to create a temporary node
function swapNodes(node1, node2) {
const temp = document.createComment('')
node2.replaceWith(temp)
node1.replaceWith(node2)
temp.replaceWith(node1)
}
Try this method:
Get the parent element
Store the two elements you want to swap
Store the .nextSibling of the node that is last in order
eg: [1,2,3,4] => we want to swap 3 & 2 then store nextSibling of 3, '4'.
.insertBefore(3,2);
.insertBefore(2,nextSibling);
Code Explanation
val & val2 are the 2 nodes/elements to be swapped
equiv(index) gets the present node/element in DOM at index passed as the paramter
NOTE: It will count comment & text elements so take care xD
Hopes this helps :)
function equiv(index){
return Array.prototype.slice.call( document.querySelectorAll("*"))[index];
}
function swap (val,val2){
let _key = val.key;
let _key_ = val2.key;
_key_ = _key < _key_ ? _key_+1:_key_;
let _parent_ = val2.parentElement.valueOf();
if (val.parentElement.children.length ==1)
val.parentElement.appendChild(val2);
else
val.parentElement.insertBefore(val2,val);
if (_parent_.children.length ==0)
_parent_.appendChild(val);
else{
let _sibling_ = equiv(_key_);
_parent_.insertBefore(val,_sibling_);}
}
A solution that works without cloning, given the indices of the two elements to swap:
function swapChildren(parentElement, index1, index2) {
if (index1 === index2)
return
if (index1 > index2) {
const temp = index1
index1 = index2
index2 = temp
}
const { [index1]: element1, [index2]: element2 } = parentElement.childNodes
if (index2 === index1 + 1) {
parentElement.insertBefore(element2, element1)
} else {
const reference = element2.nextSibling
parentElement.replaceChild(element2, element1)
parentElement.insertBefore(element1, reference)
}
}
You can swap a DOM element with its next sibling like that:
el.parentNode.insertBefore(el, el.nextElementSibling)
Or with its previous sibling like this:
el.parentNode.insertBefore(el, el.previousElementSibling)
And if your content is dynamic, you might want to check that el.nextElementSibling or el.previousElementSibling is not null.

Is it possible to get element's numerical index in its parent node without looping?

Normally I'm doing it this way:
for(i=0;i<elem.parentNode.length;i++) {
if (elem.parentNode[i] == elem) //.... etc.. etc...
}
function getChildIndex(node) {
return Array.prototype.indexOf.call(node.parentNode.childNodes, node);
}
This seems to work in Opera 11, Firefox 4, Chromium 10. Other browsers untested. It will throw TypeError if node has no parent (add a check for node.parentNode !== undefined if you care about that case).
Of course, Array.prototype.indexOf does still loop, just within the function call. It's impossible to do this without looping.
Note: If you want to obtain the index of a child Element, you can modify the function above by changing childNodes to children.
function getChildElementIndex(node) {
return Array.prototype.indexOf.call(node.parentNode.children, node);
}
Option #1
You can use the Array.from() method to convert an HTMLCollection of elements to an array. From there, you can use the native .indexOf() method in order to get the index:
function getElementIndex (element) {
return Array.from(element.parentNode.children).indexOf(element);
}
If you want the node index (as oppose to the element's index), then replace the children property with the childNodes property:
function getNodeIndex (element) {
return Array.from(element.parentNode.childNodes).indexOf(element);
}
Option #2
You can use the .call() method to invoke the array type's native .indexOf() method. This is how the .index() method is implemented in jQuery if you look at the source code.
function getElementIndex(element) {
return [].indexOf.call(element.parentNode.children, element);
}
Likewise, using the childNodes property in place of the children property:
function getNodeIndex (element) {
return [].indexOf.call(element.parentNode.childNodes, element);
}
Option #3
You can also use the spread operator:
function getElementIndex (element) {
return [...element.parentNode.children].indexOf(element);
}
function getNodeIndex (element) {
return [...element.parentNode.childNodes].indexOf(element);
}
You could count siblings...
The childNodes list includes text and element nodes-
function whichChild(elem){
var i= 0;
while((elem=elem.previousSibling)!=null) ++i;
return i;
}
There is no way to get the index of a node within its parent without looping in some manner, be that a for-loop, an Array method like indexOf or forEach, or something else. An index-of operation in the DOM is linear-time, not constant-time.
More generally, if list mutations are possible (and the DOM certainly supports mutation), it's generally impossible to provide an index-of operation that runs in constant time. There are two common implementation tactics: linked lists (usually doubly) and arrays. Finding an index using a linked list requires a walk. Finding an index using an array requires a scan. Some engines will cache indexes to reduce time needed to compute node.childNodes[i], but this won't help you if you're searching for a node. Not asking the question is the best policy.
I think you've got it, but:
make sure that variable "i" is declared with var
use === instead of == in the comparison
If you have a collection input elements with the same name (like <textarea name="text_field[]"…) in your form and you want to get the exact numeric index of the field that triggered an event:
function getElementIdxFromName(elem, parent) {
var elms = parent[elem.name];
var i = 0;
if (elms.length === undefined) // there is only one element with this name in the document
return 0;
while((elem!=elms[i])) i++;
return i;
}
Getting numeric id of an element from a collection of elements with the same class name:
function getElementIdxFromClass(elem, cl) {
var elems = document.getElementsByClassName(cl);
var i = 0;
if (elems.length > 0) {
while((elem!=elems[i])) i++;
return i;
}
return 0;
}
Try this:
let element = document.getElementById("your-element-id");
let indexInParent = Array.prototype.slice.call(element.parentNode.parentNode.children).indexOf(element.parentNode));

Categories