I am trying to build an chrome extension for lichess.org to permanently remove some elements from the website.
As the elements (in this case divs) can reappear if the user navigates through the website, I implemented a MutationObserver to remove the divs again as soon as they get added again. However, even though the function to remove them is called and they don't change their data-id, they are only removed when the function is called for the first time.
This is what I've tried so far:
These are the divs I want to remove. They are exactly the same after reappearing.
const bullet1 = document.querySelector('[data-id="1+0"]');
const bullet2 = document.querySelector('[data-id="2+1"]');
This is the MutationObserver. The div gets added with the addedNode.
const parent_lobby = document.querySelector("#main-wrap > main");
const mutationObserver = new MutationObserver(mutations => {
if (mutations[0].addedNodes[0].className == "lobby__app lobby__app-pools"){
remove_bullet_QP();
}
})
mutationObserver.observe(parent_lobby, {childList: true})
This is the function called to remove the elements. The first call of the method that happens as soon a the webiste is opened.
function remove_bullet_QP(){
bullet1.remove();
bullet2.remove();
}
remove_bullet_QP();
I've also tried to overwrite the divs before calling the function to remove them, but it didn't change the result.
Thank you for your help.
I changed bullet1 and bullet2 from const to let and reassigned them in every function call. This seems to work.
As I've said in my question, I've tried reassigning before, but without changing it to let. This should have given me an error message, somehow it didn't.
Related
In Javascript, when I click on the broom, it changes the image src to a gif and changes the ID to "walkingBroom". This works.
Then, when I click on the broom, it changes the class (this makes it walks across the screen). This does not work.
I don't know what I doing wrong - can anyone help? It's like the ID has not been changed.
document.getElementById("stillBroom").addEventListener("dblclick", animate);
function animate(){
document.getElementById("stillBroom").src = "broom.gif";
document.getElementById("stillBroom").id = "walkingBroom";}
document.getElementById("walkingBroom").addEventListener("click", walk);
function walk(){
document.getElementById("walkingBroom").className = "broom pattern0 speed1";}
You should manage the logic in a variable, like let currentState="still", then on click, change to currentState="walking", and act in consequence. Currently, you are immediately trying to attach a click event to a "walkingBroom" element that doesn't exist (it will exist, but later, after you click the broom once). For that matter, your console is very likely saying Error - Can't read property addEventListener of null and your script crashes without running. Open your console, it's very useful.
let currentState = "still";
broom.addEventListener("dblclick", clickHandler);
const clickHandler = () => {
switch(currentState) {
case "still" :
currentState="walking";
broom.src="broom.gif";
break;
case "walking":
currentState="flying";
broom.className = "broom pattern0 speed1";
break;
}
}
I am selecting multiple elements from within a page for a Chrome Extension and some of them are not being detected.
var innerChat = document.querySelector('.chat-list');
I want to appendChild to this element, but the script moves on to editing it before it even starts existing
innerChat.appendChild(emoteMenuWrap);
which obviously results in:
Uncaught TypeError: Cannot read property 'appendChild' of null
What would be the best approach for this?
Mutation observer is a good bet. For environments where this is not supported below also might do the trick since getElementsByClassName returns a live list:
var innerChat = document.getElementsByClassName("chat-list");
within your other script:
function process(){
if(!innerChat.length){
requestAnimationFrame(process);
} else {
innerChat[0].appendChild(emoteMenuWrap);
}
}
requestAnimationFrame(process);
Use a MutationObserver.
Suppose your innerChat has a parent element of chatDiv. chatDiv must exist on the page, but innerChat does not need to:
const observer = new MutationObserver(callbackFunction)
const config = { subtree: true };
observer.observe(chatDiv, config);
When the contents of chatDiv change (EG when you add innerChat to it,) observer will run the callback function.
Inside the callback function is where you would do whatever thing you wanted to wait to do.
One key to success here is to pick the parent container such that there aren't other changes going on inside it that would trigger your event more than once.
Finally, you'll probably want to remove the observer inside the callback:
observer.disconnect();
NB I edited this to suggest observing the subtree, since you're actually adding elements, not changing exiting ones.
I keep reading that there is no difference between the two but I am writing a quiz app and am seeing some differences
For example:
Quiz.prototype.handleGuess = function (id, guess) {
let button = document.getElementById(id);
button.addEventListener('click', function(){
quiz.guess(guess);
console.log(guess);
}
if(!quiz.hasEnded){
quiz.displayNext();
} else {
quiz.displayScore();
};
};
When using an event listener, I will log the guess to the console on the first button choice. When I choose an answer for the second question the console will read not only the new selection but also the choice I made from the previous question. This does not happen when using .onclick() and I am not sure why!
Consider the following code:
var el1 = document.getElementById("someEl1"),
el2 = document.getElementById("someEl2");
function firstHandler() {
alert("First handler");
}
function secondHandler() {
alert("Second handler");
}
el1.addEventListener("click", firstHandler);
el1.addEventListener("click", secondHandler);
el2.onclick = firstHandler;
el2.onclick = secondHandler;
<div id="someEl1">First Element</div>
<div id="someEl2">Second Element</div>
In case 1, clicking on el1 will alert with both messages. In case 2, clicking on el2 will only alert with the second because we overwrote what onclick does with the second assignment.
addEventListener effectively assigns a callback to some internal array of listener callbacks that will all be called whenever the event is triggered.
onclick is a single property with a single value. When you assign to it, the old value is replaced by the new assignment.
I would highly suggest that you do not use the onclick method. It makes code harder to maintain. If you are in a large code base and you set the onclick of an element and then later on another coder also sets the onclick without knowing that that element already had its onclick set, then you will run into a difficult time trying to figure out why your code is broken all of a sudden. Using the event listener pattern makes for more extensible and decoupled code.
I am trying to make a basic game on my website that involves you trying to prevent jellyfish pictures from reaching the top of the page by clicking on them to make them disappear. When I try to spawn in a jellyfish into this game however it deletes itself right away. What puzzles me more is that the javascript console logs that the element was deleted before it logs that an event handler was added. Is the jellyfish element somehow calling the delete event as soon as I set that attribute? To spawn the jellyfish on the page I click an element but the jellyfish spawn nowhere near this element if this is helpful. The playingfield parent is in a separate html file.
/*Code with the problem*/
var deleteJelly = function(jelly) {
var parent = document.getElementById("playingField");
var child = jelly;
parent.removeChild(child);
console.log("Jellyfish removed!")};
var spawnJelly = function(jellyType) {
jelliesSpawned++
var newJelly = document.createElement("img");
newJelly.setAttribute('src', "https://www.googledrive.com/host/0B-IaOP2CvHbffk56ZWFrUExfX1ZVNWZ0RmRmYU0tMHVoUHVDZzJ1NzhRV2l0c01kSENnNWc/jelly"+jellyType+".png");
document.getElementById("playingField").appendChild(newJelly);
newJelly.addEventListener("click", deleteJelly(newJelly));
console.log("added event listener")
};
/*Rest of code works fine*/
There is no need to pass jelly as the element calling the listener is this within the function. You can leverage that to simplify it to:
function deleteJelly() {
this.parentNode.removeChild(this);
console.log("Jellyfish removed!")
}
and setting the listener:
newJelly.addEventListener("click", deleteJelly);
which is a lot less code all round. ;-)
You've fallen for the classic "calling the function instead of passing the function" error.
newJelly.addEventListener("click", deleteJelly(newJelly)); // call function
vs
newJelly.addEventListener("click", function(){deleteJelly(newJelly);}); // pass function
It is deleted immediately because you have called the function, so it did what it is supposed to do: delete.
If instead you pass in an anonymous function that calls delete, then delete will not be called until the anonymous function is called - which is when you want it to happen.
I am trying too keep two instances of an Ace editor in sync. So when the user types in one, the other is updated.
Looking at their docs I see that the EditSession change event says that it returns a delta of the change, and the Document has an applyDeltas method.
So I have hooked into that change event, and when it is fired I call the other document.applyDeltas and pass it over, but it doesn't work.
I have been poking around their docs (and Google for an hour), but I am not seeing how to keep them in sync. Does anyone know how I can do that?
Ok, I figured it out. Nothing beats looking at src :)
The applyDeltas method on the document wants an array, AND you need to grab the data from the change event.
//on editor1.change
this.handleEditor1Changed = function (e) {
var deltas = new Array();
deltas[0] = e.data;
this.editor2.getSession().getDocument().applyDeltas(deltas);
};
If you looked at the source, you should have seen that applyDeltas just calls applyDelta in a loop for each element in the array. Thus, you could simply do this:
editor1.on('change', function(delta) {
editor2.session.doc.applyDelta(delta);
})
or, in more modern JavaScript
editor1.on('change', delta => editor2.session.doc.applyDelta(delta))