51

Suddenly today, out of nowhere, I started getting this one on every page on our website

Added non-passive event listener to a scroll-blocking 'touchstart' event.
Consider marking event handler as 'passive' to make the page more responsive

And its not just once or twice.. its like thousands of them....

enter image description here

They are running amok.

The only way to stop the flood of violations is to comment out this line

  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js" type="text/javascript"></script>

I read the other posts on what this violation means, but I really cannot see that I did anything different between two hours ago and now (I did a full rollback just to see if it helped)

It's almost like someone put a bug into jquery.min.js but I seriously doubt that, because then everyone would get it.

Any ideas? I tried debugging everything I could and I still have no idea what causes this?!?

UPDATE

I replaced all <button><md-tooltip>text</md-tooltip></button> with <button data-toggle="tooltip" title="text"></button> This removed 99% of all the violations.

markkus
  • 51
  • 2
  • 6
torbenrudgaard
  • 2,375
  • 7
  • 32
  • 53
  • Does it still do it with your older version? Cause that could well be a browser update that added this... – Salketer Sep 07 '17 at 11:16
  • Hmm good question... all I did actively today was to add `Speed Dial 2 - New tab 2.2.1` as a chrome browser extension (lets you control the empty page shortcuts) - but I uninstalled it just in case it was that causing problems. – torbenrudgaard Sep 07 '17 at 11:21
  • Tried running the pages on firefox, here jquery goes into infinate loop somewhere deep inside the minified code. – torbenrudgaard Sep 07 '17 at 11:30
  • 1
    Ok. Try to replace your jQuery file with the uncompressed version. You'll have a better idea when looking at the error. – Salketer Sep 07 '17 at 12:06

7 Answers7

42

This solve the problem to me:

jQuery.event.special.touchstart = {
  setup: function( _, ns, handle ){
    if ( ns.includes("noPreventDefault") ) {
      this.addEventListener("touchstart", handle, { passive: false });
    } else {
      this.addEventListener("touchstart", handle, { passive: true });
    }
  }
};
Sergio
  • 609
  • 1
  • 7
  • 11
  • 7
    Where should I add this? In the JS or after the JS include? – WilliamK Jan 23 '18 at 22:32
  • It had to load after the JS insert so that "JQuery" was declared. So I added an include for your script to load after the JS insert but I still get the same error. – WilliamK Jan 23 '18 at 22:56
  • Where exactly? Do you mean in the JQuery.js? I have min.js but even if I look at the full.js I doubt if I can tell where JQuery is loaded and where it is run. – WilliamK Jan 24 '18 at 18:24
  • You load jquery, run that code, then load the rest of your program. This code goes just after jquery and before your code. – Sergio Jan 25 '18 at 19:30
  • jQuery.min.js is loaded as an insert at the bottom of my page, followed by 6 other JS inserts. I moved your JavaScript fix to between the jQuery insert and the 2nd JS insert but nothing changed – WilliamK Jan 27 '18 at 02:38
  • Works but what is `ns`? – Frozen Crayon Apr 22 '18 at 16:12
  • 3
    It worked for me, I only had to duplicate this code in order to suppress the warning about `touchmove` (just replacing every `touchstart` with `touchmove`). As I understood from this jsbin (http://jsbin.com/bupesajoza/edit?html,js,output) the purpose of the `noPreventDefault` part is the possibility to not use a 'passive' listener. – Giorgio Tempesta Dec 14 '18 at 11:59
  • 2
    The content of if/else blocks should be swapped: if namespace includes **noPreventDefault** then passive should have a **default** value, which is **true**. – Kshatra Dec 19 '18 at 15:07
  • 1
    Another caeveat is that 'includes()' is not supported in IE11. It caused a bug that prevented all the product pages to show in our website. You can find more information about polyfills or alternative implementations here: https://stackoverflow.com/questions/31340868/includes-not-working-in-all-browsers/31340895 – Giorgio Tempesta Feb 05 '19 at 11:45
  • 2
    ie 11 compatible version: jQuery.event.special.touchstart = { setup: function( _, ns, handle ){ if ((ns.indexOf('noPreventDefault') > -1)) { this.addEventListener("touchstart", handle, { passive: false }); } else { this.addEventListener("touchstart", handle, { passive: true }); } } }; – Dmitri Tsoy Oct 28 '19 at 12:05
  • 1
    @FrozenCrayon `ns` stands probably for `namespace`. – magic_turtle Jan 01 '20 at 23:19
  • Thank you for this solution. It took care of about 1/2 of my errors (1,455 down to 729). I did the same thing for the other touch events (touchmove, etc) and it got rid of all the errors. – Ethan Brown May 24 '21 at 01:09
21

I am using various events and this seems to solve my use case

(function () {
    if (typeof EventTarget !== "undefined") {
        let func = EventTarget.prototype.addEventListener;
        EventTarget.prototype.addEventListener = function (type, fn, capture) {
            this.func = func;
            if(typeof capture !== "boolean"){
                capture = capture || {};
                capture.passive = false;
            }
            this.func(type, fn, capture);
        };
    };
}());
Artistan
  • 1,982
  • 22
  • 33
neel singh
  • 311
  • 2
  • 2
  • 1
    Good call, this worked for me. I added this to the bottom of my jQuery file called it jquery.custom.min.js and minified it. Works like a charm. – Floris Apr 11 '19 at 08:09
  • In Internet Explorer EventTarget appears to be undefined. So I would recommend adding `if (typeof EventTarget !== 'undefined') {` before the var func =` line. – Floris Apr 11 '19 at 10:45
  • I prefer this solution, but I got this error in console `[Violation] Added synchronous DOM mutation listener to a 'DOMNodeRemoved' event. Consider using MutationObserver to make the page more responsive.` Haven't figured out how to rewrite this using Mutation Observers. Please help? – Cogicero Jan 16 '21 at 22:24
  • I got topic starter's issue while animating Google Map markers. This solution works. – djdance Apr 28 '21 at 15:35
14

Ok digging this up a little more, this is not a new behavior, it has been reported a while ago and jQuery still hasn't fixed it.

The problem lies in the fact that for an handler to be passive it has to be certain of never calling preventDefault() but jQuery doesn't know in advance...

The only tip I could give you is change your console logging level and remove "Verbose". Follow up on this issue for ideas on solving this.

Salketer
  • 14,263
  • 2
  • 30
  • 58
  • the problem is the amount of violations slows the website down and we got several pages where jQuery goes into an infinate loop and spits our up to 10,000 of these violations (before I manage to close the browser). And if I comment out jQuery then everything is fine. So how do we set the handler to be `passive` ? – torbenrudgaard Sep 08 '17 at 03:23
  • Ok Salketer - found out something... `testing` is one thing that is causing this violation, and since im using a LOT of those im getting a lot of errors. Im still checking cause im sure there are move things that cause it. – torbenrudgaard Sep 08 '17 at 05:22
  • Also found that this one `` is causing a lot of these: `[Violation] Added non-passive event listener to a scroll-blocking 'wheel' event.` but ONLY when you use ng-model. – torbenrudgaard Sep 08 '17 at 05:23
  • I cannot help you much more with just those snippets. But basically, all events that causes scroll (wheel, touch, scroll) will throw this as jQuery event handling might block them. So, try removing that wheel event on your input, this is extremely weird... And see for other events. You can also reduce the amount of handlers by using event delegation. – Salketer Sep 08 '17 at 06:54
  • Right now I replaced all `` width `` This removed 99% of all the violations. – torbenrudgaard Sep 08 '17 at 07:23
  • I don't think this should be the accepted answer because of mostly the last line : "The only tip...remove 'Verbose'." - I mean, that's just bad advice. – mrk May 17 '18 at 15:32
  • @mrk I know it is not the best thing to do, but unless OP wants to rebuild the jQuery event handling system that's the only way to silence info generated by the library... I should have mentioned that it's only if you do not want to be polluted by those while working on something different... But that does not change the rest of the answer, which is still relevant. – Salketer May 18 '18 at 07:20
  • @Salketer Personally, I went with Sergio's solution which means I can keep Verbose turned on AND the issue is solved. – mrk May 21 '18 at 17:37
  • @Salketer The point being, I feel that keeping Verbose turned on can give very important information for a developer. It highlights where exactly in code that, for example, setTimeout handlers are running for a long time (and may impact the usability of the site) or when Forced reflows occur, which may be causing too many re-renders. Verbose settings gives great info and IMO, should be turned on by default in Dev Tools, because who else opens DevTools the most? I would assume developers most often keep DevTools open compared to any other user. – mrk May 21 '18 at 17:47
8

The answer from Sergio is correct, add it at the bottom jquery script. If there is issue touchstart and touchmove, just add same code and replace touchstart with touchmove, like this:

jQuery.event.special.touchstart = {
  setup: function( _, ns, handle ){
    if ( ns.includes("noPreventDefault") ) {
      this.addEventListener("touchstart", handle, { passive: false });
    } else {
      this.addEventListener("touchstart", handle, { passive: true });
    }
  }
};
jQuery.event.special.touchmove = {
  setup: function( _, ns, handle ){
    if ( ns.includes("noPreventDefault") ) {
      this.addEventListener("touchmove", handle, { passive: false });
    } else {
      this.addEventListener("touchmove", handle, { passive: true });
    }
  }
};
Hans Ganteng
  • 179
  • 1
  • 4
7

An improved solution from @neel-singh's answer

(function () {
  if (typeof EventTarget !== 'undefined') {
    let supportsPassive = false;
    try {
      // Test via a getter in the options object to see if the passive property is accessed
      const opts = Object.defineProperty({}, 'passive', {
        get: () => {
          supportsPassive = true;
        },
      });
      window.addEventListener('testPassive', null, opts);
      window.removeEventListener('testPassive', null, opts);
    } catch (e) {}
    const func = EventTarget.prototype.addEventListener;
    EventTarget.prototype.addEventListener = function (type, fn) {
      this.func = func;
      this.func(type, fn, supportsPassive ? { passive: false } : false);
    };
  }
})();
AzizStark
  • 1,390
  • 1
  • 17
  • 27
1

I think in addition to touch-based events you can add scroll-based fixes so to prevent google page score from flagging it for Desktop vs Mobile:

jQuery.event.special.wheel = {
    setup: function( _, ns, handle ){
        this.addEventListener("wheel", handle, { passive: true });
    }
};
jQuery.event.special.mousewheel = {
    setup: function( _, ns, handle ){
        this.addEventListener("mousewheel", handle, { passive: true });
    }
};
Riad N Haddad
  • 101
  • 1
  • 4
0

Just make one helper function and call it for as many events you need to call.

const preventPassiveWarning = event => {
    jQuery.event.special[event] = {
        setup: function (_, ns, handle) {
            if (ns.includes("noPreventDefault")) {
                this.addEventListener(event, handle, { passive: false });
            } else {
                this.addEventListener(event, handle, { passive: true });
            }
        }
    }
}
//Call it here 
preventPassiveWarning('touchstart')
preventPassiveWarning('touchmove')
Kh An
  • 481
  • 6
  • 13