Repository: sveltejs/svelte (JavaScript)
Date: 2019-08-03 17:57:12+00:00
Task type: Bug Fix

Issue:
`dataset` optimization does not respect null indicating to remove the attribute
**Describe the bug**
`data-foo={bar}` should, like other attribute settings, remove the attribute when setting it to a value of `null` or `undefined`. The optimization (used unless `legacy` is enabled) of instead assigning to the `dataset` object prevents this.

**To Reproduce**
```svelte
<script>
	let name = 'world';
	let toggle = false;
</script>

<input type='checkbox' bind:checked={toggle}>

<h1 data-foo={toggle ? name : null}>Hello {name}!</h1>
```

**Expected behavior**
The `data-foo` attribute should be not present when the checkbox is unchecked, not the string `'null'`.

**Severity**
Not personally affecting me.

**Additional context**
Came up in regard to [this question](https://github.com/sveltejs/svelte/issues/966#issuecomment-517905884). I believe this should be the way to handle that, but it doesn't work.



Issue discussion:
I'm not sure the extent that #858 was intended as a speed optimization vs a size optimization. The simplest solution would be to remove that optimization (to bring this in line with the other recent move to do almost everything with attributes instead of properties), but I don't know what that would lose.
If it was intended as a speed optimisation [it was misguided](https://esbench.com/bench/5d45bf9a4cd7e6009ef62702):

<img width="889" alt="Screen Shot 2019-08-03 at 13 31 06" src="https://user-images.githubusercontent.com/1162160/62415146-05c52f80-b5f3-11e9-9977-f8318a187a01.png">

I think it *was* a size thing more than anything. Dropping it seems like the straightforward option. With that change, `legacy` would only be used for readonly `input.type` properties and erroring on `once`/`passive` event modifiers.
Sounds good - I'll prepare a PR

Solution patch:
diff --git a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts
--- a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts
+++ b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts
@@ -70,11 +70,6 @@ export default class AttributeWrapper {
 
 		const is_legacy_input_type = element.renderer.component.compile_options.legacy && name === 'type' && this.parent.node.name === 'input';
 
-		const is_dataset = /^data-/.test(name) && !element.renderer.component.compile_options.legacy && !element.node.namespace;
-		const camel_case_name = is_dataset ? name.replace('data-', '').replace(/(-\w)/g, (m) => {
-			return m[1].toUpperCase();
-		}) : name;
-
 		if (this.node.is_dynamic) {
 			let value;
 
@@ -145,11 +140,6 @@ export default class AttributeWrapper {
 					`${element.var}.${property_name} = ${init};`
 				);
 				updater = `${element.var}.${property_name} = ${should_cache ? last : value};`;
-			} else if (is_dataset) {
-				block.builders.hydrate.add_line(
-					`${element.var}.dataset.${camel_case_name} = ${init};`
-				);
-				updater = `${element.var}.dataset.${camel_case_name} = ${should_cache ? last : value};`;
 			} else {
 				block.builders.hydrate.add_line(
 					`${method}(${element.var}, "${name}", ${init});`
@@ -184,9 +174,7 @@ export default class AttributeWrapper {
 					? `@set_input_type(${element.var}, ${value});`
 					: property_name
 						? `${element.var}.${property_name} = ${value};`
-						: is_dataset
-							? `${element.var}.dataset.${camel_case_name} = ${value === true ? '""' : value};`
-							: `${method}(${element.var}, "${name}", ${value === true ? '""' : value});`
+						: `${method}(${element.var}, "${name}", ${value === true ? '""' : value});`
 			);
 
 			block.builders.hydrate.add_line(statement);


Test patch:
diff --git a/test/js/samples/bind-open/expected.js b/test/js/samples/bind-open/expected.js
--- a/test/js/samples/bind-open/expected.js
+++ b/test/js/samples/bind-open/expected.js
@@ -66,4 +66,4 @@ class Component extends SvelteComponent {
 	}
 }
 
-export default Component;
+export default Component;
\ No newline at end of file
diff --git a/test/js/samples/do-use-dataset/expected.js b/test/js/samples/do-use-dataset/expected.js
--- a/test/js/samples/do-use-dataset/expected.js
+++ b/test/js/samples/do-use-dataset/expected.js
@@ -1,6 +1,7 @@
 /* generated by Svelte vX.Y.Z */
 import {
 	SvelteComponent,
+	attr,
 	detach,
 	element,
 	init,
@@ -18,8 +19,8 @@ function create_fragment(ctx) {
 			div0 = element("div");
 			t = space();
 			div1 = element("div");
-			div0.dataset.foo = "bar";
-			div1.dataset.foo = ctx.bar;
+			attr(div0, "data-foo", "bar");
+			attr(div1, "data-foo", ctx.bar);
 		},
 
 		m(target, anchor) {
@@ -30,7 +31,7 @@ function create_fragment(ctx) {
 
 		p(changed, ctx) {
 			if (changed.bar) {
-				div1.dataset.foo = ctx.bar;
+				attr(div1, "data-foo", ctx.bar);
 			}
 		},