How can I get .getAttribute and .removeAttribute to match a[onclick*='ga'] - javascript

I can't seem to get my Web Extension to block a[onclick*='ga'] as an attribute.
I've tried using
window.onload = function() {
let Removed = 0
const anchorElements = document.getElementsByTagName('A')
for (element of anchorElements) {
if (!element.getAttribute('a[onclick*='ga']')) continue
element.removeAttribute('a[onclick*='ga']')
Removed += 1
chrome.extension.sendMessage(Removed)
}
}
and
window.onload = function() {
let Removed = 0
const anchorElements = document.getElementsByTagName('A')
for (element of anchorElements) {
if (!element.getAttribute("onclick='ga'")) continue
element.removeAttribute("onclick='ga'")
Removed += 1
chrome.extension.sendMessage(Removed)
}
}
The result should be the extension removing any link with an onclick attribute of 'ga' and should then add 1 to removed which will update the extensions badge.

There are errors in your code.
Here is an example for static content. If content is generated with JavaScript, it requires additional code.
There is no need for window.onload
document.querySelectorAll('a[onclick*="ga"]').forEach(item => item.removeAttribute('onclick'));
If you want to keep a count
const a = document.querySelectorAll('a[onclick*="ga"]');
a.forEach(item => item.removeAttribute('onclick'));
chrome.runtime.sendMessage(a.length);
It is NOT a good idea to send an async message runtime.sendMessage inside the above loop.

Related

JavaScript works when setTimeout() is used, but it isn't working when document.eventListener('DOMContentLoaded', x) is used on a WordPress page. Why?

I have a few lines of JavaScript code that pick up heading texts from separate sections and place them into their respective input fields. They are also executed on single pages using wp_enqueue_script.
It works absolutely fine when setTimeout() is used:
function passengerElevator() {
var getProductName = document.querySelectorAll('[data-id="6657316"]');
getProductName.forEach(function(item) {
var productName = item.querySelector('.lift');
var inputArea = item.querySelector('input[name=product]');
inputArea.value = productName.innerText;
});
var getProductName = document.querySelectorAll('[data-id="e9c06d5"]');
getProductName.forEach(function(item) {
var productName = item.querySelector('.lift');
var inputArea = item.querySelector('input[name=product]');
inputArea.value = productName.innerText;
});
setTimeout(function() { passengerElevator() },3000);
However, there is problem of page size (some pages have more than 10 input fields) and I don't want to set an astronomically high ms to delay the script. So I decided to fire it on DOMContentLoaded:
document.addEventListener("DOMContentLoaded", passengerElevator);
function passengerElevator() {
var getProductName = document.querySelectorAll('[data-id="6657316"]');
getProductName.forEach(function(item) {
var productName = item.querySelector('.lift'); // heading text (ex:Panoramic Lift)
var inputArea = item.querySelector('input[name=product]');
inputArea.value = productName.innerText; //ouput here
});
var getProductName = document.querySelectorAll('[data-id="e9c06d5"]');
getProductName.forEach(function(item) {
var productName = item.querySelector('.lift'); // Heading text (ex:Home Lift)
var inputArea = item.querySelector('input[name=product]');
inputArea.value = productName.innerText; // Output here
});
}
As you may have already guessed, it is not working. Is my code too messy to be executed faster or is there any other problem I am missing?
I know similar questions have been asked previously, however, no existing answer I found was able to help me.
It seems like you try to loop through elements that are still not loaded. Perhaps they are being appended to the page via Ajax, so DOMContentLoaded can't help there.
You can create your own check for those elements using setInterval, so use something like this:
let dataIdCheck = setInterval(() => {
if (document.querySelectorAll('[data-id="6657316"]').length > 0 && document.querySelectorAll('[data-id="e9c06d5"]').length > 0) {
clearInterval(dataIdCheck);
// your code here
}
}, 500);
This code will run every 500 milliseconds and check if those two elements exists, using .length. Once they do exists, we stop the interval and run the code.
I also suggest to do console.log('in') to check that our interval stop running once the elements are found.

How to click an element that we get from http request using SetInterval?

window.addEventListener("load" , () => {
setInterval(() => {
let xhttp = new XMLHttpRequest();
xhttp.open("GET", "./messagedUsers.php", true);
xhttp.onload = () => {
if(xhttp.readyState === XMLHttpRequest.DONE) {
if(xhttp.status === 200) {
let data = xhttp.response;
sideActiveUsers.innerHTML = data;
}
}
}
xhttp.send();
} , 500);
messageInputContainer.focus();
})
let userInfo = document.querySelectorAll(".user-info");
userInfoClick(userInfo); // function that does operation using the nodeList userInfo
In the above code , what I'm trying to do is send a HttpRequest to the php and get a div container as a response from there. As a response , I do get the div container. The div I got from there has a class name "user-info". And I want to click it and further , apply some css to the all the elements it holds(as there will be lot of "user-info" container coming from the response.). What I'm unable to figure out is "user-info" container can be applied styling through css(not JS). But if I try to use some JS for the node with className "user-info" , nothing happens as the nodeList seems to be empty.
And I've also tried using querySelector inside the setInterval for "user-info". But that makes the function outside userInfoClick(userInfo) takes a userInfo that is not defined.
And if I use both querySelector and the function that takes the variable that holds that nodeList(userInfoClick), I am able to click the element coming back from php. But its effect is stopped after every 500 ms(as the setInterval is set for 500ms).
I want to be able to click the element coming back from php and click it whose effect doesn't change after every 500ms(along with the element getting from php every 500ms).
Help me out guys.
userInfoClick function:
const userInfoClick = (userInfo) => {
let userinfo = Array.from(userInfo);
userinfo.forEach((el , ind) => {
el.addEventListener("click" , () => {
for(let i=0 ; i<userInfo.length ; i++)
{
if(ind!==i)
{
userinfo[i].style.backgroundColor = "#dddddd";
}
}
el.style.backgroundColor = "#fff";
})
})
}
You cannot bind an event handler to an element that does not yet exist. The querySelectorAll call will return an empty collection, as the assignment to innerHTML will only happen in some future.
In this case, where you have the interval, it is better to use event delegation. This means that you listen to click events at a higher level in the DOM tree -- on an element that is always there and is not dynamically created. In your case this could be sideActiveUsers.
So change your code as follows:
Remove this line (as it will return a useless empty collection):
let userInfo = document.querySelectorAll(".user-info");
And change the userInfoClick function, which should be called only once, without any arguments:
function userInfoClick() {
sideActiveUsers.addEventListener("click" , (e) => {
const el = e.target;
// We're only interested in user-info clicks:
if (!el.classList.contains("user-info")) return;
// Since we clicked on a user-info, the following will now have matches:
for (const sibling of sideActiveUsers.querySelectorAll(".user-info")) {
sibling.style.backgroundColor = "#dddddd";
}
el.style.backgroundColor = "#fff";
});
}

"Script is broken" but works in codepen

I have a codeblock that I want to use to display projects in my portfolio that works in codepen but when I try to code it into my cargo collective site it comes back with the error that "the script is broken". The HTML and CSS are working properly only the JS is showing an error
<script>
const wheelEventName = (navigator.userAgent)
? "wheel"
: "mousewheel";
const layers = [...document.querySelectorAll(".layer")];
const tracker = document.querySelector(".track-active");
const trackerNumber = document.querySelector(".track-number");
let itemDisplayed = 0;
let animationPlaying = false;
function resetClasses() {
for (let i = 0; i < 4; i++) {
layers[0].children[i].classList.remove("item-displayed");
layers[1].children[i * 2].classList.remove("item-displayed");
}
}
document.addEventListener(wheelEventName, event => {
if (!animationPlaying) {
const nextItem = itemDisplayed + Math.sign(event.deltaY);
if (nextItem >= 0 && nextItem <= 3) {
itemDisplayed += Math.sign(event.deltaY);
layers[0].style = `transform: translateX(${-itemDisplayed * 85}vw);`;
layers[1].style = `transform: translateX(${-itemDisplayed * 85 * 2}vw);`;
layers[1].children[itemDisplayed * 2].classList.add("item-revealed");
resetClasses();
layers[0].children[itemDisplayed].classList.add("item-displayed");
layers[1].children[itemDisplayed * 2].classList.add("item-displayed");
tracker.style = `transform: translateX(${itemDisplayed * 100}%);`;
trackerNumber.innerText = `0${itemDisplayed + 1}`;
setTimeout(() => {
animationPlaying = false;
}, 2200);
animationPlaying = true;
}
}
});
</script>
Here is the codepen link that includes the HTML and CSS
https://codepen.io/pnshah115/pen/PMJBzZ
In my case I was naming a function as tryUntilDone, so I tried different changes until found the problem. There was a conflict with the name of the function. So I changed the name, and now its working:
From
function tryUntilDone () {}
to
function _tryUntilDone () {}
So the error can come from many places, but this solved my problem many times. So dont forget to try changing your variables name.
Another trick
Saving the same file more than once sometimes reports fake "script is broken" error. To avoid this error follow the steps:
Write some random code to make the script invalid. Save (it will report an error, obviously).
Reload the page
Remove the random code and save again. It will be accepted now.
Seems like executing the same script without reloading reports error and cargo thinks its an invalid script.

Not Populating list in HTML with Javascript

I am learning Javascript. I am working on reading RSS feeds for a personal project. I am using 'RSS-parser' npm library to avoid CORS error.
And also I am using Browserify bundler to make it work on the browser.
When I run this code on the terminal it gives me output without any issue. But when I try with the browser it prints nothing.
My knowledge about Asynchronous JS is limited but I am pretty sure it doesn't have errors in here as I added code to it without changing existing code.
let Parser = require('rss-parser');
let parser = new Parser();
let feed;
async () => {
feed = await parser.parseURL('https://www.reddit.com/.rss');
feedTheList();
};
// setTimeout(function() {
// //your code to be executed after 1 second
// feedTheList();
// }, 5000);
function feedTheList()
{
document.body.innerHTML = "<h1>Total Feeds: " + feed.items.length + "</h1>";
let u_list = document.getElementById("list")[0];
feed.items.forEach(item => {
var listItem = document.createElement("li");
//Add the item text
var newText = document.createTextNode(item.title);
listItem.appendChild(newText);
listItem.innerHTML =item.title;
//Add listItem to the listElement
u_list.appendChild(listItem);
});
}
Here is my HTML code.
<body>
<ul id="list"></ul>
<script src="bundle.js"></script>
</body>
Any guidance is much appreciated.
document.getElementById() returns a single element, not a collection, so you don't need to index it. So this:
let u_list = document.getElementById("list")[0];
sets u_list to `undefined, and you should be getting errors later in the code. It should just be:
let u_list = document.getElementById("list");
Also, when you do:
listItem.innerHTML =item.title;
it will replace the text node that you appended on the previous line with this HTML. Either append the text node or assign to innerHTML (or more correctly, innerText), you don't need to do both.
Looks like the async call is not being executed; You need to wrap it
in an anonymous function call:
See the example here:
https://www.npmjs.com/package/rss-parser
Essentially,
var feed; // change let to var, so feed can be used inside the function
// wrap the below into a function call
(async () => {
feed = await parser.parseURL('https://www.reddit.com/.rss');
feedTheList();
})(); // the (); at the end executes the promise
Now it will execute and feed should have items.
CORS errors when making request
As noted in the documentation at https://www.npmjs.com/package/rss-parser, if you get CORS error on a resource, use a CORS proxy. I've updated their example to fit your code:
// Note: some RSS feeds can't be loaded in the browser due to CORS security.
// To get around this, you can use a proxy.
const CORS_PROXY = "https://cors-anywhere.herokuapp.com/"
let parser = new RSSParser();
(async () => {
await parser.parseURL(CORS_PROXY + 'https://www.reddit.com/.rss', function(err, feed) {
feedTheList(feed);
});
})();
function feedTheList(feed)
{
// unchanged
}
One last thing:
The line
document.body.innerHTML = "<h1>Total Feeds: " + feed.items.length + "</h1>";
Will remove all of the content of <body>
I suggest to look into how element.appendChild works, or just place the <h1> tag in your HTML and modify its innerHTML property instead.

HTML elements made with function have same event listener

const add_compressor = (number) => {
let div = document.createElement('div');
div.className = 'compressor';
let threshold = document.createElement('input');
threshold.type = 'number';
threshold.className = 'input_number';
threshold.addEventListener('input', () => {
compressors[number].threshold.value = threshold.value;
})
div.appendChild(threshold);
added_effects.appendChild(div);
}
add_button.addEventListener('click', () => {
if (select_effect.value != 'Add Effect...') {
if (select_effect.value === 'compressor') {
compressors[compressors.length] = effects[select_effect.value];
add_compressor(compressors.length - 1);
}
}
})
I am trying to allow the creation of more than 1 compressor for the Web Audio API with this add_compressor function. Everything works except the threshold.addEventListener is linked to every compressor that is created. I am wondering if it is possible to create the event listeners for multiple compressors this way. Here is a JS fiddle showing the problem in the console.
https://jsfiddle.net/0L1my6kp/
Changing any of the input boxes for the compressors will change the threshold.value for all compressors.
The problem is that you only create a compressor object once, and reference the same object every time.
So instead of this
const effects = {
compressor: context.createDynamicsCompressor()
}
it should be
const effects = {
compressor: context.createDynamicsCompressor
}
then you create a new compressor when you click the button. Check out the updated fiddle: https://jsfiddle.net/0L1my6kp/4/

Categories

Resources