Search code examples
javascriptgoogle-chrome-extension

Log only clicked button in console | Chrome extension content.js


I want only the clicked button to be logged in the console, regardless of whether the button is newly injected or already present in the page.

I want the click event to be attached to all the buttons that are present, and only the button that was actually clicked should be logged in the console.

However on clicking the said button, all instances(all injected buutons) present in the page gets logged on console.

Below code is a part of content.js of a chrome extension under development—

const observer = new MutationObserver(function (mutationsList) {
    mutationsList.forEach(mutation => {
        if (mutation.type === "childList") {
            const toolBarDivs = document.querySelectorAll('div[aria-label*="replies"]')

            toolBarDivs.forEach(toolBarDiv => {
                if (!toolBarDiv.dataset.siblingInjected) {
                    try {
                        const newDiv = document.createElement("div")
                        newDiv.classList.add("css-175oi2r", "r-18u37iz", "r-1h0z5md", "r-13awgt0", "copy-paste-button")
                        newDiv.innerHTML = `
                          <button aria-label="Copypaste" role="button" class="css-175oi2r r-1777fci r-bt1l66 r-bztko3 r-lrvibr r-1loqt21 r-1ny4l3l" data-testid="copypaste" type="button" data-copy-paste-button-added="true">
                              <svg fill="#000000" height="18px" width="18px" viewBox="0 0 502 502">
                                  <path d="M81.5,174H247c5.523,0,10-4.477,10-10s-4.477-10-10-10H81.5c-5.523,0-10,4.477-10,10S75.977,174,81.5,174z"></path>
                                  <path d="M306.5,220h-225c-5.523,0-10,4.477-10,10s4.477,10,10,10h225c5.523,0,10-4.477,10-10S312.023,220,306.5,220z"></path>
                                  <path d="M306.5,286h-225c-5.523,0-10,4.477-10,10s4.477,10,10,10h225c5.523,0,10-4.477,10-10S312.023,286,306.5,286z"></path>
                                  <path d="M306.5,352h-225c-5.523,0-10,4.477-10,10s4.477,10,10,10h225c5.523,0,10-4.477,10-10S312.023,352,306.5,352z"></path>
                                  <path d="M306.5,154h-22c-5.523,0-10,4.477-10,10s4.477,10,10,10h22c5.523,0,10-4.477,10-10S312.023,154,306.5,154z"></path>
                                  <path d="M481,379.417h-47.713c-2.364,0-4.287-1.923-4.287-4.287v-9.98c0-9.858-5.885-18.666-14.993-22.438c-9.108-3.772-19.497-1.706-26.468,5.265L377,358.516V103c0-2.652-1.054-5.196-2.929-7.071l-93-93C279.196,1.054,276.652,0,274,0H21c-5.523,0-10,4.477-10,10v482c0,5.523,4.477,10,10,10h346c5.523,0,10-4.477,10-10v-7.681l10.539,10.539c4.658,4.657,10.841,7.125,17.149,7.125c3.133,0,6.297-0.609,9.319-1.861c9.107-3.772,14.993-12.58,14.993-22.438v-9.98c0-2.364,1.923-4.287,4.287-4.287H481c5.523,0,10-4.477,10-10v-64C491,383.894,486.523,379.417,481,379.417z M284,34.142L342.858,93H284V34.142z M357,482H31V20h233v83c0,5.523,4.477,10,10,10h83v265.516l-35.831,35.83c-1.406,1.407-2.351,3.189-2.735,5.111c-0.128,0.641-0.194,1.297-0.194,1.96c0,2.652,1.054,5.196,2.929,7.071L357,464.319V482z M471,443.417h-37.713c-13.392,0-24.287,10.895-24.287,24.287v9.98c0,2.493-1.658,3.551-2.646,3.961c-0.99,0.409-2.911,0.833-4.672-0.93l-59.299-59.298l59.298-59.298c1.763-1.762,3.684-1.338,4.672-0.929c0.988,0.409,2.646,1.468,2.646,3.96v9.98c0,13.392,10.895,24.287,24.287,24.287H471V443.417z"></path>
                              </svg>
                          </button>
                      `
                        toolBarDiv.insertBefore(newDiv, toolBarDiv.children[3] || null)
                        toolBarDiv.dataset.siblingInjected = "true"
                    } catch (error) {
                        console.error("Error injecting button:", error)
                    }
                }
            })
        }
    })

    const allButtons = document.querySelectorAll('button[data-testid="copypaste"]')

    allButtons.forEach(button => {
        button.addEventListener("click", event => {
            console.log("Button clicked:", event.target)
        })
    })
})

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

Later,

I tried this(outside mutation observer logic) also didn't works—

document.body.addEventListener('click', function(event) {
  if (event.target.tagName === 'BUTTON' && event.target.hasAttribute('data-testid') && event.target.getAttribute('data-testid') === 'copypaste') {
    console.log('Button clicked:', event.target);
  }
});

Solution

  • The idea is correct, but the 'click' event is not suitable as it cannot be captured this way.

    You may need some corrections:

    window.addEventListener('mouseup', event => { 
        if (event.target.constructor == HTMLButtonElement
            && event.target.hasAttribute('data-testid'))
               console.log('Button clicked:', event.target);
               // and log something else
    });
    

    This is just the idea. You may need some improvements. First, with this code, you can record some false-positive events because it may or may not be a click. It could be a non-click 'mouseup' when you press a mouse button somewhere else, move the mouse pointer to a button element, and then release the mouse button. To filter out such cases, you may need to handle also 'mousedown', track the down state of a button, and so on. This is a fairly simple technique, I think you can develop it. In many cases, you can simply ignore this false-positive problem.

    Also, event.target in console.log won't tell you anything, so you would need to log some button properties and other useful information, most likely, testid. I think this is clear.

    The alternative solution: It looks like you don't really use allButtons. You can use it: traverse the button set and add the same 'click' handler to each. Make sure you add a handler only once. This is the case where you can use simple button.onclick = event => {}; but then you should not forget to chain it with your functional semantic handler. Also, you would need to add the same handler every time you inject a button. This approach could be easier overall.

    Finally, see the comment by @woxxom and fix the code accordingly, to avoid possible repetition of the same code execution on each call to the constructor MutationObserver.