Firefox addon got removed because of unsafe assignment to innerHTML. Replaced it with the Jquery append(), but it is also unsafe. What can I use instead?
I need to add content dynamically to the extension's DOM. Mozilla gave me this link when they took down the addon: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Safely_inserting_external_content_into_a_page
But I can not figure out from the link how to add nodes to an existing node in a safe way.
This is an example of what i'm doing now. I have several places where I append a node to another node. In this case I need to make a div for every category and add it to the node "folders"
var folders = $("#folders");
for (var i = 0; i < categories.length; i++) {
var s = categories[i];
var categoryFolder = $("<div>");
categoryFolder.addClass("folder");
categoryFolder.attr("id",s);
categoryFolder.text(s);
folders.append(categoryFolder);
}
Related
I'm using Material Components Web to create a website. I have a list of button with the class .mdc-button and I activated it with the following line into my Javascript File.
window.button = new mdc.ripple.MDCRipple.attachTo(document.querySelector('.mdc-button'));
The problem is that this only applies to the first element with the class .mdc-button
Why is that and how to fix it?
document.querySelector will always return the first instance of the element found within the DOM. You can use document.getElementsByClassName('mdc-button') to return a full list of classes within the DOM.
https://www.w3schools.com/jsref/met_document_getelementsbyclassname.asp
Perhaps something like this
var x = document.getElementsByClassName('mdc-button');
var i;
for (i = 0; i < x.length; i++) {
mdc.ripple.MDCRipple.attachTo(x[i]);
}
Here is an ES6 way of doing the same thing (for anyone else reading this post):
const buttons = document.querySelectorAll('.mdc-button');
buttons.forEach(button => MDCRipple.attachTo(button));
JavaScript Newbie so the following question maybe 'dumb'.
For my work I do a little GreaseMonkey scripting to make our eCommerce store back end a little more friendly to our customer service team. Normally I'd make a new or better UI for them setup a special role or something, but with this current system, this is literally not an option.
Specifically I make a selection of links visibility: hidden, as they trigger some reporting functions which can lock up the backend till the reports are completed.
Up to this point I have had the script load jQuery, which is fine but not ideal. Then I dug in and saw that the interface was built using YUI, while this is already loaded, the syntax is weird to me, and I don't like it much, plus it is now not supported.
Recently I found the Plain JS site, which describes how to use 'vanilla' JS to do jQuery like things. Splendid! I thought, Now I can just write simple JS without extra dependencies or libs! But this is not quite the case.
I have tried the following:
var links = Array.from(document.querySelectorAll("a")); // creates an actual array from the node list returned by document.query
var links_to_hide = links.slice(14, 22); // gets just the bits we want to affect from the array, and is still an array
// ok so 'links_to_hide' is an array, and it is an array of 'a' anchor tags.
// if I go into the inspector and set the visibility property it affects the tag but doing it via scripting seems to not work.
// so if links_to_hide is an array it should be possible to
for(var i = links_to_hide.length; i <= links_to_hide.length; i++){
links_to_hide.style['visibility'] = 'hidden';
}
// this for loop doesn't seem to actually affect anything
What am I missing. Near as I can tell this should work.
Change this:
for(var i = links_to_hide.length; i <= links_to_hide.length; i++){
links_to_hide.style['visibility'] = 'hidden';
}
To this
for(var i = 0; i < links_to_hide.length; i++){
links_to_hide[i].style['visibility'] = 'hidden';
}
You aren't using i
links_to_hide[i].style.visibility = 'hidden';
Your for loop should be like below:
for(var i = 0; i < links_to_hide.length; i++){
links_to_hide[i].style.visibility = 'hidden';
}
I am currently building a Chrome extension which has to find specific pages in a website specifically the Log In / Sign In page, the Sign Up / Register page, the About page and the Contact Us page.
I am trying to achieve this by first getting the list of elements in the page (which I have already done). Now I need to check the innerHTML of the element such that it is a leaf node in the DOM and contains a part of the keyword, and I am trying to do this with a regex. I managed to build a regex which successfully returns what's in between a start or end tag of an element (i.e. the tag name along with its attributes), but not the innerHTML. Below is what I have done so far (with the example for the About page:
var list = document.body.getElementsByTagName("*");
var aboutElement = /^[^<.+>].*About.*[^(<.+>]$/i;
for (var i = 0; i <= list.length; i++) {
if ((aboutElement.test(list[i].innerHTML)) || (aboutElement.test(list[i].alt))) {
list[i].click();
}
}
Any idea what I should add to it such that it only matches leaf nodes (nodes which do not contain other nodes) and not what's in a start or end tag? I also think that with what I've done it's going to match everything in the innerHTML because of the .* part so I may need to change that as well. Any help would be greatly appreciated!
Thanks to two of the answers in the comments I managed to solve the problem. I used .textContent and changed the regex as shown below and it worked.
var list = document.body.getElementsByTagName("*");
var aboutElement = /^(.*?\s*(\bAbout\b)[^$]*)$/i;
for (var i = 0; i <= list.length; i++) {
if ((aboutElement.test(list[i].textContent)) || (aboutElement.test(list[i].alt))) {
list[i].click();
}
}
Just to clarify what I'm trying to do, I'm trying to make a Chrome extension that can loop through the HTML of the current page and remove html tags containing certain text. But I'm having trouble looping through every html tag.
I've done a bunch of searching for the answer and pretty much every answer says to use:
var items = document.getElementsByTagName("*");
for (var i = 0; i < items.length; i++) {
//do stuff
}
However, I've noticed that if I rebuild the HTML from the page using the elements in "items," I get something different than the page's actual HTML.
For example, the code below returns false:
var html = "";
var elems = document.getElementsByTagName("*");
for (var i = 0; i < elems.length; i++) {
html += elems[i].outerHTML;
}
alert(document.body.outerHTML == html)
I also noticed that the code above wasn't giving ALL the html tags, it grouped them into one tag, for example:
var html = "";
var elems = document.getElementsByTagName("*");
alert(elems[0].outerHTML);
I tried fixing the above by recurssively looking for an element's children, but I couldn't seem to get that to work.
Ideally, I would like to be able to get every individual tag, rather than ones wrapped in other tags. I'm kind of new to Javascript so any advice/explanations or example code (In pure javascript if possible) as to what I'm doing wrong would be really helpful. I also realize my approach might be completely wrong, so any better ideas are welcome.
What you need is the famous Douglas Crockford's WalkTheDOM:
function walkTheDOM(node, func)
{
func(node);
node = node.firstChild;
while (node)
{
walkTheDOM(node, func);
node = node.nextSibling;
}
}
For each node the func will be executed. You can filter, transform or whatever by injecting the proper function.
To remove nodes containing a specific text you would do:
function removeAll(node)
{
// protect against "node === undefined"
if (node && node.nodeType === 3) // TEXT_NODE
{
if (node.textContent.indexOf(filter) !== -1) // contains offending text
{
node.parentNode.removeChild(node);
}
}
}
You can use it like this:
filter = "the offending text";
walkTheDOM(document.getElementsByTagName("BODY")[0], removeAll);
If you want to parametize by offending text you can do that, too, by transforming removeAll into a closure that is instantiated.
References to DOM elements in JavaScript are references to memory addresses of the actual nodes, so you can do something like this (see the working jsfiddle):
Array.prototype.slice.call(document.getElementsByTagName('*')).forEach(function(node) {
if(node.innerHTML === 'Hello') {
node.parentNode.removeChild(node);
}
});
Obviously node.innerHTML === 'Hello' is just an example, so you'll probably want to figure out how you want to match the text content (perhaps with a RegEx?)
I want to add an <img> to every <td> tag with a certain CSS Class in my page.
I used "querySelectorAll" to look them up, like this:
var painted = document.querySelectorAll('.painted');
Now I would like to add to each one of them a specific image with a unique ID. I assume I need to loop through the list somehow and edit each element's innerHTML, could anyone provide the syntax for that?
Thanks
Just run it like a normal for loop, and add properties to each element.
for (var i = 0, len = painted.length; i < len; i++) {
painted[i].id = "foo" + i;
painted[i].innerHTML = "<strong>Your content</strong>";
}
This uses innerHTML to create new content. If you need more complex content processing then there's no single syntax. You need to learn the DOM API and perform the needed manipulations.
For example, if you wanted to add an image, you can create one and append it directly.
for (var i = 0, len = painted.length; i < len; i++) {
var img = document.createElement("img");
img.id = "myimage" + i;
painted[i].appendChild(img);
}
Notice that I'm not using HTML markup. A DOM node doesn't have "HTML content". It is part of an object tree structure, so it has child nodes, which have their own child nodes, and so on.
So what you need to do, is perform some DOM selection with the current painted element as the root, and decide what should go where.