What's wrong with my Greasemonkey script?

I run the following script with Greasemoneky. The script uses me to display:none or visibility:hidden some html objects in Facebook.

// ==UserScript==
// @name        facebook
// @namespace   nms
// @include     http://*.facebook.com/*
// @include     https://*.facebook.com/*
// @version     1
// @grant       none
// ==/UserScript==

	document.querySelector("._1uh-").style.display = "none";

	// everythingGeneral:

		document.querySelectorAll("#left_nav_section_nodes, .fbChatSidebar").forEach( (e)=> {
			element.style.visibility = "hidden";
	});

As for my humble knowledge in JS and Greasemonkey, it seems to me okay but as a beginner I might miss something. The includes are in place. The code is just fine (targeted my elements and either display:none or visibility:hidden).

Also, no related error is in console.

Any ideas what I missed?

So what’s the problem you’re having with it?

It doesn’t work. It doesn’t give the effect even though the script is on in Greasemonkey (which is on by itself).

Should

element.style.visibility = “hidden”;

not be

e.style.visibility = “hidden”;

You where right. I missed that. From now and long I will search for discrepancies between the name of the argument and what’s done with this argument below.

It is interesting though that the code still doesn’t work.

If I had an alert("hi); above document.querySelector("._1uh-").style.display = "none"; the alert works but if I add it right under it, it won’t work.

This is wired for me because I know that either the code is okay (fully executable) or it doesn’t, and also, that row seems okay to me.

This probably means that document.querySelector("._1uh-") didn’t find a matching element and thus returned null; and trying to set .style.display of null will throw a type error and the following code won’t run. So if you’re not absolutely sure the element exists by the time your script runs, you’d have to check if there actually was a result first:

var element = document.querySelector('._1uh-')

if (element) {
  element.style.display = 'none'
}

It would also be important to note that facebook is an SPA with little to no static content; basically the entire page is generated dynamically with JS, so most elements won’t be there from the start. Now if you want to hide certain elements that are created dynamically, you might observe the whole document.body with a mutation observer, and hide these elements when they’re actually getting appended to the DOM.

1 Like

You are right. I had a mistake there with the name because now the code generally works. What you say is very interesting to me.

As I understand, if I delete the elements that contains the data that emerges from the SPA behavior, these data won’t appear. For example, if I have a ul element to which all li elements are loaded from SPA, I could display:none or visibility:hidden this element, so that it won’t contain the SPA-loaded li children.

Yet, the real problem is when the ul and it’s li children are created together on the fly, as in the SPA.

I understand I have 2 ways to combat this:

  1. Using a setInterval function to load the dispaly:none or visitbility:hidden each time anew for the newley created ul+li.

  2. Targeting the event that triggers creation of the ul + li element to load the dispaly:none or visitbility:hidden each time anew, yet, I believe this second solution requires deep react.js knowledge I don’t have at the moment.

That would be possible too, although I’d assume that a mutation observer would be more efficient than actively polling the DOM for changes all the time. It might look like

const checkAndHideNode = node => {
  if (node.matches('.some-selector')) {
    node.style.display = 'none'
  }
}

const checkRecord = record => {
  Array.from(record.addedNodes).forEach(checkAndHideNode)
}

const checkMutations = mutations => {
  mutations.forEach(checkRecord)
}

const observer = new MutationObserver(checkMutations)

observer.observe(document.body, {
  childList: true,
  subtree: true
})

You could also just observe a specific parent element instead of the whole document.body, but this would of course require that this element already exists.

I don’t think it’s possible to listen to React synthetic events from “outside”… and even if it was (say by means of a native DOM event listener), you still wouldn’t know when the new element got actually appended to the DOM.

you still wouldn’t know when the new element got actually appended to the DOM.

If you listen to that particular event, and the code runs anytime it is triggered, how can’t you know when the new elements appended to DOM?

Because the event handler will most likely not modify the DOM directly, but request a change to the application state. React will then figure out how (and when) to update the UI efficiently; for instance, it may delay the changes to apply multiple updates in a single batch.

This is of course very React-specific, but generally speaking you can’t know when an element will be appended to the DOM when there is some asynchronicity involved. For example, if you don’t append an element immediately but request an animation frame to do so:

const addToListButton = document.querySelector('.add-to-list-button')
const myList = document.querySelector('.my-list')

addToListButton.addEventListener('click', event => {
  event.preventDefault()

  window.requestAnimationFrame(() => {
    const newLiElement = document.createElement('li')

    newLiElement.innerHTML = 'some content'
    myList.appendChild(newLiElement)
  })
})

Another vanilla JS example would be if the creation of that list item would require some preliminary AJAX requests:

addToListButton.addEventListener('click', event => {
  event.preventDefault()

  fetch('my/endpoint')
    .then(res => res.json())
    .then(data => {
      const newLiElement = document.createElement('li')

      newLiElement.innerHTML = data.html
      myList.appendChild(newLiElement)
    })
})

So even if you knew when the event occurred, you still wouldn’t know when the AJAX response arrived and you could query for the new element.

The problem was due to the fact Facebook loads each page in two possible different ways: AJAJ or from PHP.

Opening an internal link with the left click of the mouse causes serving it with AJAJ and open it with a middle (wheel) click, from PHP.

This worked without any problem:

let utilityFunc = ()=> {
    var run = (run)=> {
       // insert your code here
    };

    var pS = window.history.pushState;
    var rS = window.history.replaceState;

    window.history.pushState = (a, b, url)=> {
        run(url);
        pS.apply(this, arguments);
    };

    window.history.replaceState = (a, b, url)=> {
        run(url);
        rS.apply(this, arguments);
    };

utilityFunc();

Expand here:

https://forum.tampermonkey.net/viewtopic.php?f=17&t=2247

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.