Skip to content

Commit 7503d12

Browse files
medusalixsindresorhus
authored andcommitted
Add event clearing to prefer-add-event-listener rule (#216)
1 parent e403caf commit 7503d12

File tree

3 files changed

+73
-17
lines changed

3 files changed

+73
-17
lines changed

docs/rules/prefer-add-event-listener.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# Prefer `addEventListener` over `on`-functions
1+
# Prefer `addEventListener` and `removeEventListener` over `on`-functions
22

3-
Enforces the use of, for example, `foo.addEventListener('click', handler);` over `foo.onclick = handler;` for HTML DOM Events. There are [numerous advantages of using `addEventListener`](https://stackoverflow.com/questions/6348494/addeventlistener-vs-onclick/35093997#35093997). Some of these advantages include registering unlimited event handlers and optionally having the event handler invoked only once.
3+
Enforces the use of `addEventListener` and `removeEventListener` over their `on` counterparts. For example, `foo.addEventListener('click', handler);` is preferred over `foo.onclick = handler;` for HTML DOM Events. There are [numerous advantages of using `addEventListener`](https://stackoverflow.com/questions/6348494/addeventlistener-vs-onclick/35093997#35093997). Some of these advantages include registering unlimited event handlers and optionally having the event handler invoked only once.
44

5-
This rule is fixable.
5+
This rule is fixable (only for `addEventListener`).
66

77

88
## Fail
@@ -19,6 +19,10 @@ foo.onkeydown = () => {};
1919
foo.bar.onclick = onClick;
2020
```
2121

22+
```js
23+
foo.onclick = null;
24+
```
25+
2226
## Pass
2327

2428
```js
@@ -32,3 +36,7 @@ foo.addEventListener('keydown', () => {});
3236
```js
3337
foo.bar.addEventListener('click', onClick);
3438
```
39+
40+
```js
41+
foo.removeEventListener('click', onClick);
42+
```

rules/prefer-add-event-listener.js

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ const getEventTypeName = eventMethodName => eventMethodName.slice('on'.length);
99

1010
const beforeUnloadMessage = 'Use `event.preventDefault(); event.returnValue = \'foo\'` to trigger the prompt.';
1111

12-
const formatMessage = (eventMethodName, extra) => {
13-
let message = `Prefer \`addEventListener\` over \`${eventMethodName}\`.`;
12+
const formatMessage = (methodReplacement, eventMethodName, extra) => {
13+
let message = `Prefer \`${methodReplacement}\` over \`${eventMethodName}\`.`;
1414

1515
if (extra) {
1616
message += ' ' + extra;
@@ -42,6 +42,18 @@ const shouldFixBeforeUnload = (assignedExpression, nodeReturnsSomething) => {
4242
return !nodeReturnsSomething.get(assignedExpression);
4343
};
4444

45+
const isClearing = node => {
46+
if (node.type === 'Literal') {
47+
return node.raw === 'null';
48+
}
49+
50+
if (node.type === 'Identifier') {
51+
return node.name === 'undefined';
52+
}
53+
54+
return false;
55+
};
56+
4557
const create = context => {
4658
const nodeReturnsSomething = new WeakMap();
4759
let codePathInfo = null;
@@ -83,22 +95,25 @@ const create = context => {
8395
return;
8496
}
8597

86-
if (eventTypeName === 'beforeunload' &&
98+
if (isClearing(assignedExpression)) {
99+
context.report({
100+
node,
101+
message: formatMessage('removeEventListener', eventMethodName)
102+
});
103+
} else if (eventTypeName === 'beforeunload' &&
87104
!shouldFixBeforeUnload(assignedExpression, nodeReturnsSomething)
88105
) {
89106
context.report({
90107
node,
91-
message: formatMessage(eventMethodName, beforeUnloadMessage)
108+
message: formatMessage('addEventListener', eventMethodName, beforeUnloadMessage)
109+
});
110+
} else {
111+
context.report({
112+
node,
113+
message: formatMessage('addEventListener', eventMethodName),
114+
fix: fixer => fix(fixer, context.getSourceCode(), node, memberExpression)
92115
});
93-
94-
return;
95116
}
96-
97-
context.report({
98-
node,
99-
message: formatMessage(eventMethodName),
100-
fix: fixer => fix(fixer, context.getSourceCode(), node, memberExpression)
101-
});
102117
}
103118
};
104119
};

test/prefer-add-event-listener.js

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const expectedBeforeUnloadWithReturnMessage = [
2626
ruleTester.run('prefer-add-event-listener', rule, {
2727
valid: [
2828
'foo.addEventListener(\'click\', () => {})',
29+
'foo.removeEventListener(\'click\', onClick)',
2930
'foo.onclick',
3031
'foo.setCallBack = () => {console.log(\'foo\')}',
3132
'setCallBack = () => {console.log(\'foo\')}',
@@ -39,11 +40,21 @@ ruleTester.run('prefer-add-event-listener', rule, {
3940
'foo.addEventListener(\'click\', () => {})',
4041
'onclick'
4142
),
43+
invalidTestCase(
44+
'foo.onclick = 1',
45+
'foo.addEventListener(\'click\', 1)',
46+
'onclick'
47+
),
4248
invalidTestCase(
4349
'foo.bar.onclick = onClick',
4450
'foo.bar.addEventListener(\'click\', onClick)',
4551
'onclick'
4652
),
53+
invalidTestCase(
54+
'const bar = null; foo.onclick = bar;',
55+
'const bar = null; foo.addEventListener(\'click\', bar);',
56+
'onclick'
57+
),
4758
invalidTestCase(
4859
'foo.onkeydown = () => {}',
4960
'foo.addEventListener(\'keydown\', () => {})',
@@ -63,7 +74,30 @@ ruleTester.run('prefer-add-event-listener', rule, {
6374
})`,
6475
'onclick'
6576
),
66-
77+
invalidTestCase(
78+
'foo.onclick = null',
79+
null,
80+
null,
81+
'Prefer `removeEventListener` over `onclick`.'
82+
),
83+
invalidTestCase(
84+
'foo.onclick = undefined',
85+
null,
86+
null,
87+
'Prefer `removeEventListener` over `onclick`.'
88+
),
89+
invalidTestCase(
90+
'window.onbeforeunload = null',
91+
null,
92+
null,
93+
'Prefer `removeEventListener` over `onbeforeunload`.'
94+
),
95+
invalidTestCase(
96+
'window.onbeforeunload = undefined',
97+
null,
98+
null,
99+
'Prefer `removeEventListener` over `onbeforeunload`.'
100+
),
67101
invalidTestCase(
68102
'window.onbeforeunload = foo',
69103
null,
@@ -92,7 +126,6 @@ ruleTester.run('prefer-add-event-listener', rule, {
92126
null,
93127
expectedBeforeUnloadWithReturnMessage
94128
),
95-
96129
invalidTestCase(
97130
`window.onbeforeunload = function () {
98131
return;

0 commit comments

Comments
 (0)