Search code examples
javascriptjquerytwitteraddeventlistener

Javascript click event listener fires only once


I have a Chrome extension that intercepts and checks tweets before they get posted. To do this, I've add an event listener to the Tweet button. Sine the content is dynamic, I use the solution proposed in this thread:

initialize : function() {
    let that = this;
    let jsInitChecktimer = setInterval(checkForJsFinished, 111);
    function checkForJsFinished () {
        if (document.querySelector("div[data-testid='tweetButtonInline']")) {          
            clearInterval (jsInitChecktimer);
            console.log("Button found");
            that.addSubmitNewTweetClickHandler();
        }
    }
},

addSubmitNewTweetClickHandler : function() {
    let that = this;
    let buttonSubmitTweet = document.querySelector("div[data-testid='tweetButtonInline']");
    buttonSubmitTweet.addEventListener('click', function(e) {
        console.log("CLICK");
        // Stop default event from happening
        e.preventDefault();
        e.stopImmediatePropagation();
        // Do stuff
    });
},

If the tweet passed the checks alright, it gets submitted by programmatically triggering the event using .trigger('click').

This works fine, but only once. After a tweet has been submitted and posted, the event listener on the Tweet button is gone, and I cannot intercept the next tweet to check it. I've tried calling initialize() after submitted again -- maybe the button gets removed and newly added to the DOM (it actually disappears fire a moment when submitting a tweet) -- but the querySelector finds the button immediately. But even after calling initialize() again, no click even on the Tweet button fires.

What could be the issue here? My problem is that I don't even know where to look for and how to debug this.


Solution

  • After many more hours, I've finally figured it out. The problem was essentially the highly dynamic content of the new Twitter website. After submitting a tweet, the Tweet button gets indeed removed and added again. In needed to do a serious of changes:

    • Use a MutationObserver to keep track of any changes. Every time there's a change, call the initialize() function. To avoid too many calls, I do this in case of certain changes (unnecessary detail here)

    • Change the addSubmitNewTweetClickHandler() method so that the event listener first gets removed in order to avoid duplicate listeners (please note that I use objects hence the use of this compared to my original question)

      addSubmitNewTweetClickHandler : function() {
        let that = this;
        let buttonSubmitTweet = document.querySelector("div[data-testid='tweetButtonInline']");
        buttonSubmitTweet.removeEventListener('click', this.handleSubmitNewTweetClick ); 
        this.handleSubmitNewTweetClick = this.handleSubmitNewTweetClick.bind(this)
        buttonSubmitTweet.addEventListener('click', this.handleSubmitNewTweetClick );    
      },
      

      This change required to create the reference function handleSubmitNewTweetClick

    Overall, it's still not a perfect solution since I call initialize() many unnecessary time. However, I failed to reliably identify when the Tweet button was added to the document. When I used the MutationObserver none of the added nodes had the attribute data-testid which I need to identify the correct button. I have node idea why this attribute was not there. Maybe the attribute is added some times after added to button, but even with an additional MutationObserver looking for attribute changes I could detect this.

    Anyway, it works now and it's only for a prototype.