Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Kristján Oddsson <koddsson@gmail.com>
- Loading branch information
Showing
3 changed files
with
109 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# No Useless Passive | ||
|
||
This rule disallows setting `passive: true` for events on which it will have no effect. | ||
|
||
Events where `passive: true` has an effect are: `touchstart`, `touchmove`, `wheel`, and `mousewheel`. | ||
|
||
```js | ||
// bad (passive has no effect here) | ||
window.addEventListener('scroll', () => { /* ... */ }, { passive: true }) | ||
|
||
// good | ||
window.addEventListener('scroll', () => { /* ... */ }) | ||
``` | ||
|
||
## Why? | ||
|
||
Adding `passive: true` informs the browser that this event will not be calling `preventDefault` and as such is safe to asynchronously dispatch, freeing up the main thread for lag-free operation. However many events are not cancel-able and as such setting `passive: true` will have no effect on the browser. | ||
|
||
It is safe to leave the option set, but this may have a negative effect on code authors, as they might believe setting `passive: true` has a positive effect on their operations, leading to a false-confidence in code with `passive: true`. As such, removing the option where it has no effect demonstrates to the code author that this code will need to avoid expensive operations as this might have a detrimental affect on UI performance. | ||
|
||
## See Also | ||
|
||
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Improving_scrolling_performance_with_passive_listeners |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
const passiveEventListenerNames = new Set(['touchstart', 'touchmove', 'wheel', 'mousewheel']) | ||
|
||
const propIsPassiveTrue = prop => prop.key && prop.key.name === 'passive' && prop.value && prop.value.value === true | ||
|
||
module.exports = { | ||
meta: { | ||
docs: {}, | ||
fixable: 'code' | ||
}, | ||
|
||
create(context) { | ||
return { | ||
['CallExpression[callee.property.name="addEventListener"]']: function(node) { | ||
const [name, listener, options] = node.arguments | ||
if (name.type !== 'Literal') return | ||
if (passiveEventListenerNames.has(name.value)) return | ||
if (options && options.type === 'ObjectExpression') { | ||
const i = options.properties.findIndex(propIsPassiveTrue) | ||
if (i === -1) return | ||
const passiveProp = options.properties[i] | ||
const l = options.properties.length | ||
const source = context.getSourceCode() | ||
context.report({ | ||
node: passiveProp, | ||
message: `"${name.value}" event listener is not cancellable and so \`passive: true\` does nothing.`, | ||
fix(fixer) { | ||
const removals = [] | ||
if (l === 1) { | ||
removals.push(options) | ||
removals.push(...source.getTokensBetween(listener, options)) | ||
} else { | ||
removals.push(passiveProp) | ||
if (i > 0) { | ||
removals.push(...source.getTokensBetween(options.properties[i - 1], passiveProp)) | ||
} else { | ||
removals.push(...source.getTokensBetween(passiveProp, options.properties[i + 1])) | ||
} | ||
} | ||
return removals.map(t => fixer.remove(t)) | ||
} | ||
}) | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
const rule = require('../lib/rules/no-useless-passive') | ||
const RuleTester = require('eslint').RuleTester | ||
|
||
const ruleTester = new RuleTester() | ||
|
||
ruleTester.run('no-useless-passive', rule, { | ||
valid: [ | ||
{ | ||
code: 'document.addEventListener("scroll", function(event) {})' | ||
}, | ||
{ | ||
code: 'document.addEventListener("resize", function(event) {})' | ||
}, | ||
{ | ||
code: 'document.addEventListener("resize", function(event) {}, { passive: false })' | ||
} | ||
], | ||
invalid: [ | ||
{ | ||
code: 'document.addEventListener("scroll", function(event) {}, { passive: true })', | ||
output: 'document.addEventListener("scroll", function(event) {} )', | ||
errors: [ | ||
{ | ||
message: '"scroll" event listener is not cancellable and so `passive: true` does nothing.', | ||
type: 'Property' | ||
} | ||
] | ||
}, | ||
{ | ||
code: 'document.addEventListener("scroll", function(event) {}, { passive: true, foo: 1 })', | ||
output: 'document.addEventListener("scroll", function(event) {}, { foo: 1 })', | ||
errors: [ | ||
{ | ||
message: '"scroll" event listener is not cancellable and so `passive: true` does nothing.', | ||
type: 'Property' | ||
} | ||
] | ||
} | ||
] | ||
}) |