I'm writing a Chrome content script extension and I need to be able to target a specific element that, unfortunately, has no unique identifiers except its parent element.
I need to target the immediate first child element of parentElement. console.log(parentElement) reports both of the child elements/nodes perfectly, but the succeeding console logs (the ones that target the childNodes) always return an undefined value no matter what I do.
This is my code so far
(I have excluded the actual names to avoid confusion and extra, unnecessary explanation)
function injectCode() {
var parentElement = document.getElementsByClassName("uniqueClassName");
if (parentElement && parentElement.innerHTML != "") {
console.log(parentElement);
console.log(parentElement.firstElementChild);
console.log(parentElement.firstChild);
console.log(parentElement.childNodes);
console.log(parentElement.childNodes[0]);
console.log(parentElement.childNodes[1]);
} else {
setTimeout(injectCode, 250);
}
}
How do I select the first child element/node of parentElement?
Update:
parentElement.children[0] also has the same error as parentElement.childNodes[0].
Both these will give you the first child node:
console.log(parentElement.firstChild); // or
console.log(parentElement.childNodes[0]);
If you need the first child that is an element node then use:
console.log(parentElement.children[0]);
Edit
Ah, I see your problem now; parentElement is an array.
If you know that getElementsByClassName will only return one result, which it seems you do, you should use [0] to dearray (yes, I made that word up) the element:
var parentElement = document.getElementsByClassName("uniqueClassName")[0];
Related
I have an element E and I'm appending some elements to it. All of a sudden, I find out that the next element to append should be the first child of E. What's the trick, how to do it? Method unshift doesn't work because E is an object, not array.
Long way would be to iterate through E's children and to move'em key++, but I'm sure that there is a prettier way.
var eElement; // some E DOM instance
var newFirstElement; //element which should be first in E
eElement.insertBefore(newFirstElement, eElement.firstChild);
2018 version - prepend
parent.prepend(newChild) // [newChild, child1, child2]
This is modern JS! It is more readable than previous options. It is currently available in Chrome, FF, and Opera.
The equivalent for adding to the end is append, replacing the old appendChild
parent.append(newChild) // [child1, child2, newChild]
Advanced usage
You can pass multiple values (or use spread operator ...).
Any string value will be added as a text element.
Examples:
parent.prepend(newChild, "foo") // [newChild, "foo", child1, child2]
const list = ["bar", newChild]
parent.append(...list, "fizz") // [child1, child2, "bar", newChild, "fizz"]
Related DOM methods
Read More - child.before and child.after
Read More - child.replaceWith
Mozilla Documentation
Can I Use
2017 version
You can use
targetElement.insertAdjacentElement('afterbegin', newFirstElement)
From MDN :
The insertAdjacentElement() method inserts a given element node at a given position relative to the element it is invoked upon.
position
A DOMString representing the position relative to the element; must be one of the following strings:
beforebegin: Before the element itself.
afterbegin: Just inside the element, before its first child.
beforeend: Just inside the element, after its last child.
afterend: After the element itself.
element
The element to be inserted into the tree.
In the family of insertAdjacent there is the sibling methods:
element.insertAdjacentHTML('afterbegin','htmlText')`
That can inject html string directly, like innerHTML but without override everything, so you can use it as a mini-template Engin and jump the oppressive process of document.createElement and even build a whole component with string manipulation process
element.insertAdjacentText for inject sanitize string into element . no more encode/decode
You can implement it directly i all your window html elements.
Like this :
HTMLElement.prototype.appendFirst = function(childNode) {
if (this.firstChild) {
this.insertBefore(childNode, this.firstChild);
}
else {
this.appendChild(childNode);
}
};
Accepted answer refactored into a function:
function prependChild(parentEle, newFirstChildEle) {
parentEle.insertBefore(newFirstChildEle, parentEle.firstChild)
}
Unless I have misunderstood:
$("e").prepend("<yourelem>Text</yourelem>");
Or
$("<yourelem>Text</yourelem>").prependTo("e");
Although it sounds like from your description that there is some condition attached, so
if (SomeCondition){
$("e").prepend("<yourelem>Text</yourelem>");
}
else{
$("e").append("<yourelem>Text</yourelem>");
}
I think you're looking for the .prepend function in jQuery. Example code:
$("#E").prepend("<p>Code goes here, yo!</p>");
I created this prototype to prepend elements to parent element.
Node.prototype.prependChild = function (child: Node) {
this.insertBefore(child, this.firstChild);
return this;
};
var newItem = document.createElement("LI"); // Create a <li> node
var textnode = document.createTextNode("Water"); // Create a text node
newItem.appendChild(textnode); // Append the text to <li>
var list = document.getElementById("myList"); // Get the <ul> element to insert a new node
list.insertBefore(newItem, list.childNodes[0]); // Insert <li> before the first child of <ul>
https://www.w3schools.com/jsref/met_node_insertbefore.asp
I have this in jQuery:
$(document).bind("click touchstart", function(e) {
if (e.target.class != searchControls && !searchControls.find(e.target).length) {
//do something
});
});
What this does is checks the click and if it's not the search element a child of that element, then I can run something. What I'm trying to do is find a pure JavaScript alternative to this.
So far I have:
var searchTrigger = document.querySelector(".header__search-btn"),
productSearch = document.querySelector(".product-search"),
searchControls = document.querySelectorAll(".product-search__positioner");
document.onclick = function (e) {
if (e.target.class != searchControls && e.parentNode != searchControls)
// do something
}
}
However this still fires my function inside the if statement, so clearly I'm going wrong—not sure how wrong. Help appreciated.
document.querySelectorAll returns a collection of DOM elements. e.target.class != searchControls doesn't make sense (ignoring that target.class doesn't even exist): Assuming you mean className, you are trying to compare a string with a list of DOM elements. That will always be false.
So lets ignore that part and look at e.parentNode != searchControls. The event object doesn't have a property parentNode. Again, even if you meant e.target.parentNode, it would not make much sense, since you are testing whether a single element is identical to a list of elements.
Based in your use of .find, you basically want to know whether a node is contained in another node. Every DOM node has a method .contains, so all you have to do is iterate over the collection of DOM elements and call that method:
var contained = false;
for (var i = 0; i < searchControls.length; i++) {
if (searchControls[i].contains(e.target)) {
contained = true;
break;
}
}
This can easily be moved into its own reusable function.
There is no e.target.class. If you are checking for class name then it should be
e.target.className
Then to check for parent elements class name
e.target.parentNode.className
if you are looking to search inside the parent node then use
e.target.parentNode.querySelector("pass your selector here")
if you want to search for just child elements:
e.target.querySelector("pass your selector here")
I'm trying to remove the text node "one".
$targetDiv is jQuery object
$targetDiv[0].outerHTML
<div><div>one<font face="Impact" size="4">www</font></div></div>
$targetDiv[0].innerHTML
<div>one<font face="Impact" size="4">www</font></div>
I can remove the other text node "www" like below:
$targetDiv.find("font").each(function ()
{
if (this.firstChild.nodeType === 3)
{
this.firstChild.data = "";
}
});
But having tough time removing the "one" part.
$targetDiv[0].firstChild
<div>
$targetDiv[0].firstChild.data
undefined
$targetDiv[0].firstChild.innerText
undefined
$targetDiv[0].firstChild.innerHTML
"one<font face="Impact" size="4">www</font>"
$targetDiv[0].firstChild.innerText
undefined
$targetDiv[0].firstChild.textContent
"onewww"
It's not very clear what context this is in, or what $targetDiv is, but based on the results you're getting we can assume you are using Firefox, and that $targetDiv is the first div, and that this should work
$($targetDiv.find("div").get(0).firstChild).remove();
FIDDLE
You can use the native .removeChild method to remove the first text node:
var div=targetDiv[0].firstChild;
div.removeChild(div.firstChild);
This method is invoked from the parent of the node that you wish to remove, and receives the node to remove as its argument.
Modern browsers let you simply call .remove() on the node itself.
target[0].firstChild.firstChild.remove();
Your .each() loop was close. In that particular case, since the one text node is the only sibling that is a text node, you'd just iterate the children under target since there's only one.
$targetDiv.children("div").each(function ()
{
if (this.firstChild.nodeType === 3)
{
this.firstChild.data = "";
}
});
Though targeting it directly instead of a loop makes more sense in this case.
Try,
$($targetDiv.children('div').contents()[0]).remove();
DEMO
I'm am battling with a javascript function I'm working on.
Inside a for loop I'm iterating all elements with class "visible", inside that loop
I'm preforming two actions.
elements[i].removeAttribute("class");
elements[i].setAttribute("class", "hidden");
For some reason only 1 is valid. 2 produces an error saying:
Uncaught TypeError: Cannot call method 'setAttribute' of undefined
Even when I log elements[i] using console.log; after the first console.log call
the element exists, but on the second console.log elements[i] is 'undefined'
What the hell am I missing here, this is driving me crazy, if my laptop wasn't so expensive
it would have been broken by now. Help :(
Here's the function:
function hide_visable_elements()
{
// remove body EventListener
var body = document.getElementsByTagName("body");
body[0].removeEventListener("click", hide_visable_elements, true);
var elements = document.getElementsByClassName("visible");
for (var i = 0; i < elements.length; i++)
{
console.log(elements[i]); // Works like a swiss clock
elements[i].removeAttribute("class");
console.log(elements[i]); // why elements[i] is 'undefined' now ???
elements[i].setAttribute("class", "hidden"); // << turns to useless code
}
}
This is because getElementsByClassName returns a NodeList, which is live. That is, it updates itself when the elements it refers to change.
When you remove the class attribute from an element in the NodeList, it gets removed from that list (since it no longer has the visible class name).
You don't actually need to remove the attribute. Just setting it will do the job just as well. But since the NodeList is changing as you manipulate the elements it contains, you need to count backwards through it (as each time you change one element of it, it is removed so the length decreases by one):
for (var i = elements.length - 1; i >= 0; i--) {
elements[i].setAttribute("class", "hidden");
}
getElementsByClassName is a live NodeList so changing className of the items immediately affects whole list. I would recommend use querySelectorAll insead.
Plus instead of var body = document.getElementsByTagName("body"); use document.body.
I think that the problem is elements[i].removeAttribute("class"); since you selected the element using a class getElementsByClassName("visible"); . I think so when you remove class attribute completely from the element things are going wrong.
Try some tweak with the code. You are not suppose to remove attribute class if you are planning to use the same element which is selected using class attribute.
I have an element E and I'm appending some elements to it. All of a sudden, I find out that the next element to append should be the first child of E. What's the trick, how to do it? Method unshift doesn't work because E is an object, not array.
Long way would be to iterate through E's children and to move'em key++, but I'm sure that there is a prettier way.
var eElement; // some E DOM instance
var newFirstElement; //element which should be first in E
eElement.insertBefore(newFirstElement, eElement.firstChild);
2018 version - prepend
parent.prepend(newChild) // [newChild, child1, child2]
This is modern JS! It is more readable than previous options. It is currently available in Chrome, FF, and Opera.
The equivalent for adding to the end is append, replacing the old appendChild
parent.append(newChild) // [child1, child2, newChild]
Advanced usage
You can pass multiple values (or use spread operator ...).
Any string value will be added as a text element.
Examples:
parent.prepend(newChild, "foo") // [newChild, "foo", child1, child2]
const list = ["bar", newChild]
parent.append(...list, "fizz") // [child1, child2, "bar", newChild, "fizz"]
Related DOM methods
Read More - child.before and child.after
Read More - child.replaceWith
Mozilla Documentation
Can I Use
2017 version
You can use
targetElement.insertAdjacentElement('afterbegin', newFirstElement)
From MDN :
The insertAdjacentElement() method inserts a given element node at a given position relative to the element it is invoked upon.
position
A DOMString representing the position relative to the element; must be one of the following strings:
beforebegin: Before the element itself.
afterbegin: Just inside the element, before its first child.
beforeend: Just inside the element, after its last child.
afterend: After the element itself.
element
The element to be inserted into the tree.
In the family of insertAdjacent there is the sibling methods:
element.insertAdjacentHTML('afterbegin','htmlText')`
That can inject html string directly, like innerHTML but without override everything, so you can use it as a mini-template Engin and jump the oppressive process of document.createElement and even build a whole component with string manipulation process
element.insertAdjacentText for inject sanitize string into element . no more encode/decode
You can implement it directly i all your window html elements.
Like this :
HTMLElement.prototype.appendFirst = function(childNode) {
if (this.firstChild) {
this.insertBefore(childNode, this.firstChild);
}
else {
this.appendChild(childNode);
}
};
Accepted answer refactored into a function:
function prependChild(parentEle, newFirstChildEle) {
parentEle.insertBefore(newFirstChildEle, parentEle.firstChild)
}
Unless I have misunderstood:
$("e").prepend("<yourelem>Text</yourelem>");
Or
$("<yourelem>Text</yourelem>").prependTo("e");
Although it sounds like from your description that there is some condition attached, so
if (SomeCondition){
$("e").prepend("<yourelem>Text</yourelem>");
}
else{
$("e").append("<yourelem>Text</yourelem>");
}
I think you're looking for the .prepend function in jQuery. Example code:
$("#E").prepend("<p>Code goes here, yo!</p>");
I created this prototype to prepend elements to parent element.
Node.prototype.prependChild = function (child: Node) {
this.insertBefore(child, this.firstChild);
return this;
};
var newItem = document.createElement("LI"); // Create a <li> node
var textnode = document.createTextNode("Water"); // Create a text node
newItem.appendChild(textnode); // Append the text to <li>
var list = document.getElementById("myList"); // Get the <ul> element to insert a new node
list.insertBefore(newItem, list.childNodes[0]); // Insert <li> before the first child of <ul>
https://www.w3schools.com/jsref/met_node_insertbefore.asp