I am surprised there isn't some cookbook code ready that enumerates or searches along the preceding-axis in a DOM with javascript the way that XPath can do it. And of course, the following-axis as well.
Example problem is: I have a text node, and I want to find the preceding text node.
Obviously the precedingSibling is insufficient, because there could be none, or it could be an element.
I have implemented that several times for myself, but am too lazy to try to find my code. Instead I prefer doing it over again. But also like to compare notes, and surprised that nobody seems to ask about it, or show off theirs.
So here is a function signature with some beginning scribble:
Node.prototype.findPrevious function(foundPr = x => x, giveUpPr = () => false) {
node = this;
while(true) {
let previousSibling = node.previousSibling;
if(!previousSibling) // no previousSibling means we need to go up
while(true) {
const parent = node.parentNode;
previousSibling = parent.previousSibling;
if(parent.previousSibling)
break;
node = parent;
if(!node || giveUpPr(node))
return null;
}
// now we have the nearest previous sibling (or we gave up already)
node = previousSibling;
// but we aren't done, we have to go down now
// now finally we have the last previous item
while(true) {
let lastChild = node.lastChild;
if(!lastChild)
break;
node = lastChild;
}
// now our node is the deepest lastChild that doesn't have any more children
// now only can we check our found predicate:
if(foundPr(node))
return node;
// and if not we go to the beginning
}
};
I guess I just wrote the code instead of explaining what it would do. And the cool thing is that the same logic works the other way around for findNext:
Node.prototype.findNext = function(foundPr = x => x, giveUpPr = () => false) {
let node = this;
while(true) {
let nextSibling = node.nextSibling;
if(!nextSibling) // no nextSibling means we need to go up
while(true) {
const parent = node.parentNode;
nextSibling = parent.nextSibling;
if(nextSibling)
break;
node = parent;
if(!node || giveUpPr(node))
return null;
}
// now we have the nearest next sibling (or we gave up already)
node = nextSibling;
// but we aren't done, we have to go down now
// now finally we have the first next item
while(true) {
let firstChild = node.firstChild;
if(!firstChild)
break;
node = firstChild;
}
// now our node is the deepest firstChild that doesn't have any more children
// now only can we check our found predicate:
if(foundPr(node))
return node;
// and if not we go to the beginning trying the nextSibling
}
};
But isn't there some built-in solution already?
Related
I am really having trouble thinking. I am trying to code but mostly copy and paste as I am trying to learn. Hear is the code I've put together so far.
function LinkedList() {
this.head = null;
this.length++;
}
function Node(val) {
this.val = val;
this.next = null;
}
LinkedList.prototype.add = function(val) {
let newNode = new Node(val);
newNode.next = this.head;
this.head = newNode;
this.length++;
return this.head;
};
LinkedList.prototype.delete = function(val) {
let current = this.head;
while (current.value !== val) {
current = current.next;
}
if (current.next) {
current.value = current.next.val;
current.next = current.next.next;
}
};
let linkedList = new LinkedList();
linkedList.add(1);
linkedList.add(2);
console.log(linkedList.delete(1))
When I console log the last line it says "Cannot read properties of undefined (reading 'value')
P.S. I don't even know how people "low g" is that even possible?
Thank you
Several issues:
The loop condition current.value !== val will be true for all nodes because current.value is undefined. It should be current.val.
Since the loop condition is always true, eventually current will be null, and then current.value will trigger the error you got. You should actually expect this to happen also when the above error is fixed, because delete may be called with a value that is not present in the list, and then also the loop will run until current is null. You should exit before this happens.
The logic you have used to delete a node consists of copying the next node's value, and removing the next node from the list. But this will not work when the node to delete is the very last one. In that case your code will run into an error. Moreover, this is a structural problem: there is no next node to delete, so you'll end up with the same number of nodes. You should really stop the search one node earlier and then delete the next node.
this.length++ in the LinkedList constructor makes no sense, since the this.length property does not exist yet when ++ is executed. Moreover, there is no node yet in this list, so the length should just be initialised to 0.
The delete method should adjust this.length when the deletion is successful.
Doing a console.log of what delete returns is useless: delete is not returning anything, so you are just going to see undefined in the console. It is not clear why and what you wanted to print. You could let the delete method return a boolean, so to indicate whether a node was deleted or not (in case it was not found). If the idea was to print the list after the deletion, then you need to do that separately, and first need to provide a method that allows printing a linked list in a nice way, otherwise you'll just get an object representation as output.
The add method should not return the head node. This is not very useful, as the caller should not be bothered with Node instances. If you want to return anything, then return the linked list itself (this). That way you can allow someone to chain add methods, like linkedlist.add(1).add(2).
Not a problem, but the syntax you have used for creating a constructor and prototype functions is old-fashioned. Since ECMAScript2015 we can make use of the so much nicer class syntax. So I suggest you use it.
Here is a new version of your code taking all of the above into account:
class Node { // Use class syntax
constructor(val) {
this.val = val;
this.next = null;
}
}
class LinkedList {
constructor() {
this.head = null;
this.length = 0; // There are no nodes yet
}
add(val) {
let newNode = new Node(val);
newNode.next = this.head;
this.head = newNode;
this.length++;
return this; // Don't return the head
};
delete(val) {
let current = this.head;
// current could be null, so protect against run time errors
if (!current) return false; // Indicate that no deletion occurred
if (current.val === val) {
this.head = current.next;
this.length--; // adjust length
return true; // Indicate success!
}
// Your algorithm can never delete the last node, so we use
// a different algorithm, which looks one node ahead
while (current.next) {
if (current.next.val == val) { // Found it
// Delete it
current.next = current.next.next;
this.length--; // adjust length
return true; // success
}
current = current.next;
}
}
// Make the linked list iterable. This helps with printing
*[Symbol.iterator]() { // Special property name used by iteration
for (let node = this.head; node; node = node.next) {
yield node.val;
}
}
}
let linkedList = new LinkedList();
linkedList.add(1).add(2).add(3).add(4);
// Print the list by triggering an iteration
console.log(...linkedList); // 4 3 2 1
if (linkedList.delete(2)) console.log("successfully deleted 2");
console.log(...linkedList); // 4 3 1
console.log("this list has", linkedList.length, "items");
I need to use recursion to navigate every element in the DOM, and for every body element, determine if it is an element node. If it is, I need to add a child node to it. I need to use JavaScript for this assignment. Here is what I have so far in my JavaScript file:
window.addEventListener("load", function() {
var highlightButton = document.getElementById("highlight");
highlightButton.addEventListener('click', search);
function search(node) {
if (node.nodeType === 1) {
var spanEl = document.createElement("span");
spanEl.className = "hoverNode";
node.appendChild(spanEl);
spanEl.innerHTML = spanEl.parentNode.tagName;
}
}
})
I understand how to append a child node, but I don't know how to traverse the entire DOM and append the child node to every element.
Given "every body element" actually means "every element in the body", you can start with an element and get all its child elements. Loop over the child elements and if any is type 1 and has a child nodes, you call the function again with the element.
If it doesn't have children, go to the next child, etc. The following is just an example of recursing over all the nodes and picking out just the type 1s. You can modify it to do whatever.
// Call with a document or HTML element
function checkBodyElements(node) {
// Recursive function
function traverseBody(node) {
if (node.childNodes.length) {
// Loop over every child node
node.childNodes.forEach(child => {
// If it's a type 1, call the function recursively
if (child.nodeType == 1) {
console.log(child.tagName, child.nodeType)
traverseBody(child);
}
});
}
}
// Get the body element
let body = node.querySelector('body');
// If a body element was found, traverse its children
if (body) {
traverseBody(body);
}
}
window.onload = checkBodyElements(document);
<div>
<div>
<p><span></span>
</p>
</div>
<div>
<p><span></span>
</p>
</div>
</div>
Is there any specific reason that implies the creation of a recursive function, other than to support older browsers, like IE6 and 7?
If no, you could simply use document.body.querySelectorAll('*') to select every element node in the DOM while ignoring the ones outside the body element. An example would be:
window.addEventListener('load', function () {
var highlightButton = document.getElementById("highlight");
function search () {
document.body.querySelectorAll('*').forEach(function (el) {
var spanEl = document.createElement('span');
spanEl.innerHTML = el.tagName;
spanEl.className = 'hoverNode';
el.appendChild(spanEl);
});
}
highlightButton.addEventListener('click', search);
});
If yes, then an option would be:
window.addEventListener('load', function () {
var highlightButton = document.getElementById("highlight");
// traverse downwards from rootEl while excluding Comment elements on IE6 7 & 8
function traverseDOMFrom (rootEl, iterator) {
if (!rootEl || rootEl.nodeType !== 1 || typeof iterator !== 'function') {
return;
}
if (rootEl.children && rootEl.children.length > 0) {
Array.prototype.slice.call(rootEl.children).forEach(function (el) {
traverseDOMFrom(el, iterator);
});
}
iterator(rootEl);
}
function search () {
traverseDOMFrom(document.body, function (el) {
var spanEl = document.createElement('span');
spanEl.innerHTML = el.tagName;
spanEl.className = 'hoverNode';
el.appendChild(spanEl);
});
}
highlightButton.addEventListener('click', search);
});
Notice that in both cases, a polyfill for Array.prototype.forEach() and for EventTarget.addEventListener() would be necessary if you want to support those features on IE6, 7 and 8! Otherwise you could also achieve the same results by iterating the element's array with a custom for loop instead, as for this .addEventListener, a simple .onload event handler could be used if there's no need to support multiple listeners.
How can I remove all content between two tags that they aren't in standard tags;
<!--googleoff: index-->
some codes and content here...
<!--googleon: index-->
This is an ads that showing in one site and I'd like to block and remove theme in browser by User JS
Those are comment nodes, not tags. The best thing would probably be to identify the parent, then loop through the children; see comments:
// Assuming a single parent
let parent = document.querySelector(".stuff");
if (parent) {
// Uncomment if you want to see nodes before the change
// showNodes("before", parent);
let removing = false;
let child = parent.firstChild;
let next = null;
// While we still have child elements to process...
while (child) {
// If we're already removing, remember that
let removeThis = removing;
// Before we remove anything, identify the next child to visit
next = child.nextSibling;
// Is this a comment node?
if (child.nodeType === Node.COMMENT_NODE) {
if (child.nodeValue.includes("googleoff: index")) {
// It's the node that tells us to start removing:
// Turn on our flag and also remove this node
removing = true;
removeThis = true;
} else if (child.nodeValue.includes("googleon: index")) {
// It's the node that tells us to stop removing:
// Turn off our flag, but do remove this node
removing = false;
removeThis = true;
}
}
if (removeThis) {
// This is either stuff in-between the two comment nodes
// or one of the comment nodes; either way, remove it
parent.removeChild(child);
}
// Move on to next child
child = next;
}
// Uncomment if you want to see nodes before the change
// showNodes("after", parent);
}
Live Example:
// Brief delay so you can see it happen
setTimeout(() => {
// Assuming a single parent
let parent = document.querySelector(".stuff");
if (parent) {
// Uncomment if you want to see nodes before the change
// showNodes("before", parent);
let removing = false;
let child = parent.firstChild;
let next = null;
// While we still have child elements to process...
while (child) {
// If we're already removing, remember that
let removeThis = removing;
// Before we remove anything, identify the next child to visit
next = child.nextSibling;
// Is this a comment node?
if (child.nodeType === Node.COMMENT_NODE) {
if (child.nodeValue.includes("googleoff: index")) {
// It's the node that tells us to start removing:
// Turn on our flag and also remove this node
removing = true;
removeThis = true;
} else if (child.nodeValue.includes("googleon: index")) {
// It's the node that tells us to stop removing:
// Turn off our flag, but do remove this node
removing = false;
removeThis = true;
}
}
if (removeThis) {
// This is either stuff in-between the two comment nodes
// or one of the comment nodes; either way, remove it
parent.removeChild(child);
}
// Move on to next child
child = next;
}
// Uncomment if you want to see nodes before the change
// showNodes("after", parent);
}
}, 800);
function showNodes(label, parent) {
console.log(label);
for (let child = parent.firstChild; child; child = child.nextSibling) {
console.log(child.nodeType, child.nodeValue);
}
}
<div>
Just some content that isn't related
</div>
<div class="stuff">
This is the parent element
<!--googleoff: index-->
some codes and content here...
<!--googleon: index-->
</div>
If this stuff appears in more than one place, obviously wrap that in a loop.
If you can't identify the parent, you'll have to walk the DOM all the way through, which is a bit more work (recursive function), but not that bad.
My solution works well if the starting node is passed to the function correctly. I want to know if my solution is good and efficient. I should be able to return true if the cycle exists via function to which first node is passed as parameter. I would like to know if my solution is efficient especially for an interview setting. My comments in the code are self explanatory. Im using a variable track to traverse through the list and checking for a null or head as the next. If i encounter any of them traversal ends and then individually i check for null or head condition and based on that i return the appropriate boolean value.
function SLLNode(elem) {
this.value=elem;
this.next=null;
}
var hasCycle=function(node){
var track=node;
//traverse thru list till next node is either null or back to first node
while(track.next!==null && track.next!==this.head){
track=track.next;
}
if(track.next === null){ //if next node null then no cycle
return false;
}
if(track.next===this.head){ //if next node head then there is cycle
return true;
}
}
var my_node1=new SLLNode(3);
var my_node2=new SLLNode(5);
var my_node3=new SLLNode(19);
//assigning head
var head=my_node1;
//connecting linked list
my_node1.next=my_node2;
my_node2.next=my_node3;
my_node3.next=my_node1; //cycle
console.log("Has cycle?: "+hasCycle(my_node1)); //outputs true as expected
var node1=new SLLNode(3);
var node2=new SLLNode(5);
var node3=new SLLNode(19);
//assigning head
var head1=node1;
node1.next=node2;
node2.next=node3;
console.log("Has cycle?: "+hasCycle(node1)); //outputs false as expected
JSON.stringify() can be used to detect cyclic linked lists. CircularDetector returns true if the linked list is cyclic.
function CircularDetector (head) {
try {
JSON.stringify(head);
return false;
} catch (e) {
return true;
}
}
You can read more on cycle detection at https://en.wikipedia.org/wiki/Cycle_detection but the main takeaway is that if you move one pointer twice as fast as another pointer then a loop would be identifiable as the fast pointer will eventually catch up with the other. Here's a possible solution in js.
function hasCycle(head) {
var slow, fast;
if(!head || !head.next) return false;
slow = head;
fast = head;
if(head.next === head) return true;
while(fast.next.next) {
slow = slow.next;
fast = fast.next.next;
if(slow === fast) return true;
}
return false;
}
Not a very efficient solution as I am using map but if you don't want to use two pointers this solution is easy to understand
// Preparation code:
class Node {
constructor(value) {
this.value = value;
this.next = null;
}
}
function hasCycle(head) {
let node = head;
let map={};
while(node){
if(map[node.value]){
//return node or true
return {"Found":node}
}
else{
map[node.value] = true;
}
node = node.next;
}
return "Not found";
}
const nodeA = new Node('A');
const nodeB = nodeA.next = new Node('B');
const nodeC = nodeB.next = new Node('C');
const nodeD = nodeC.next = new Node('D');
const nodeE = nodeD.next = new Node('E');
console.log(hasCycle(nodeA)); // => null
nodeE.next = nodeB;
console.log(hasCycle(nodeA))
I was doing a challenge of building a tree from all html elements. And I am 90% done, but I got stuck...
How do I change this string into a tree?:
mystring= "red1/(none)-red2/red1-blue1/red2-blue2/red2-blue3/red2-red3/red1-red4/red3-red5/red4-red6/red5-blue4/red6";
After splitting them by "-" we will have:
10 groups of -> (parameter1)/(parameter2)
The first parameter it is the object,
The second parameter is the 'in-what-it-will-be-contained'
I have no idea how to move every 'parameter1' inside 'parameter2'. (note: sometimes the parameter1 will be the parameter2 of a parameter1)
Visual example of what I mean with a parameter is inside another parameter: (this example uses exactly the string above)
Probably we should use arrays?, idk... I am totally lost :sadface:
I think this is a little more concise and straight forward. It uses an object as a dictionary to lookup the parent, rather than a function that has to recursively iterate the tree to find the parent. That recursive function is expensive. An object lookup is quick.
First, for convenience, I'd define an object type:
function TreeNode(name) {
this.Name = name;
this.Children = [];
}
Then I'd add a method to do the work. This parses your tree string:
TreeNode.ParseTree = function (treeString) {
var root = new TreeNode("");
var nodes = {};
var pairs = treeString.split("-");
pairs.forEach(function(pair) {
var parts = pair.split("/");
var parentName = parts[1];
var childName = parts[0];
var node;
if (parentName == "(none)") {
node = root;
root.Name = childName;
}
else {
node = new TreeNode(childName);
nodes[parentName].Children.push(node);
}
nodes[childName] = node;
});
return root;
};
That's it! Now, to get visual representations of your tree, you can add some prototype methods to TreeNode. First, override .toString():
TreeNode.prototype.toString = function(indent) {
indent = indent || "";
var strings = [indent + this.Name];
this.Children.forEach(function(child) {
strings.push(child.toString(indent + " "));
});
return strings.join("\n");
};
Then, add a .Render() method to display the tree within a web page:
TreeNode.prototype.Render = function(container) {
var nodeEl = container.appendChild(document.createElement("div"));
nodeEl.className = "treeNode";
var nameEl = nodeEl.appendChild(document.createElement("div"));
nameEl.className = "treeNodeName";
nameEl.appendChild(document.createTextNode(this.Name));
var childrenEl = nodeEl.appendChild(document.createElement("div"));
childrenEl.className = "treeNodeChildren";
this.Children.forEach(function(child) {
child.Render(childrenEl);
});
return nodeEl;
};
Here it is in action: http://jsfiddle.net/gilly3/wwFBx/
Edit: I didn't notice the jQuery tag in your post, here's a render method that's all jQuery, and produces simpler HTML which you seem to imply is what you want:
TreeNode.prototype.Render = function(container) {
var el = $("<div>").appendTo(container).text(this.Name);
$.each(this.Children, function() {
this.Render(el);
});
return el;
};
This JSFiddle uses jQuery, even replacing Array.forEach with $.each: http://jsfiddle.net/wwFBx/1/
As an alternative, you might consider just serializing your tree as JSON. Eg:
"{\"Name\":\"red1\",\"Children\":[{\"Name\":\"red2\",\"Children\":[{\"Name\":\"blue1\",\"Children\":[]},{\"Name\":\"blue2\",\"Children\":[]},{\"Name\":\"blue3\",\"Children\":[]}]},{\"Name\":\"red3\",\"Children\":[{\"Name\":\"red4\",\"Children\":[{\"Name\":\"red5\",\"Children\":[{\"Name\":\"red6\",\"Children\":[{\"Name\":\"blue4\",\"Children\":[]}]}]}]}]}]}"
or maybe:
"{\"red1\":{\"red2\":{\"blue1\":{},\"blue2\":{},\"blue3\":{}},\"red4\":{\"red5\":{\"red6\":{\"blue4\":{}}}}}}"
Parse the string via JSON.parse().
Disclaimer: I've referenced Array.forEach() and JSON.parse() which are built-in to modern browsers but are not supported by older browsers. To enable these functions in older browsers, see this documentation on Array.forEach() and this shim for JSON.parse().
Here's about how I would do it, using an array of "unplaced" elements and looping through it until they're all placed:
var str = "red1/(none)-red2/red1-blue1/red2-blue2/red2-blue3/red2-red3/red1-red4/red3-red5/red4-red6/red5-blue4/red6";
var unplaced = [];
var tree = null;
var elements = str.split(/[\/\-]/);
function findNodeByName(nodeName, context) {
if(context.name === nodeName) return context;
for(var i = 0; i < context.children.length; i++) {
var subSearch = findNodeByName(nodeName, context.children[i]);
if(subSearch) return subSearch;
}
return null;
}
var element, node, parent, thisElement, i;
for(i = 0; node = elements[i]; i += 2) {
node = elements[i];
parent = elements[i + 1];
thisElement = {name: node, children: []};
if(!tree && parent === '(none)') {
tree = thisElement;
} else if(tree) {
var parentNode = findNodeByName(parent, tree);
if(parentNode) {
parentNode.children.push(thisElement);
} else {
unplaced.push(thisElement);
}
}
}
var oldLength;
while(unplaced.length) {
oldLength = unplaced.length;
for(i = 0; element = unplaced[i]; i++) {
var parentNode = findNodeByName(parent, tree);
if(parentNode) {
parentNode.children.push(element);
unplaced.splice(i, 1);
i--;
}
}
if(oldLength === unplaced.length) {
throw new SyntaxError("The string is not a valid tree.");
}
}
// The result is contained in "tree".
You can see the result at: http://jsfiddle.net/minitech/tJSpN/
One with a function: http://jsfiddle.net/minitech/tJSpN/1/
And one with more error-checking: http://jsfiddle.net/minitech/tJSpN/2/
Actually, I found a simpler/shorter/neater way using the JQuery function AppendTo()
We just need to:
Split the parameters...
Create one div for each (parameter1)
Use a loop to move every (parameter1) inside (parameter2) using the
AWESOME AppendTo() function that JQuery offers
The best thing is that they are actually inside them, so you can easily put a Hide/Show effect to make a cool effect
You may try to create tree nodes of the form :
node = {
str:"red1",
subBranches : new Array()
}
Once you have that, you may add the sub-branches iterating through the array, adding such nodes for each found correct couple, and removing the couples already placed in rootNode.subBranches. Then you recursively do the same for every sub-branche.