This even leaves enough space in the tweet for <input type=submit value=Add> if you wanted a button beside the initial text box.
As you see, it’d be shorter as straight HTML without the “body must be empty, you can only write inside a <script> tag” requirement, as you could just drop the document.write("…") wrapping.
Note that HN apparently ate your "delete" character(s) (happened to me with "recycle" sign as well).
Props for HTML escaping, was not in task, as well as separate delete buttons. Note though that entities in the document.write are transformed, so the "html escaping" is broken in effect; either double escaping or codepoints should make it work.
Or, you know, the mighty glorious ancient deprecated "example" element, that is still very well supported. Provided you are OK with danger of '</xmp>'and will not prevent it, result would be quite terse:
I used U+2718 HEAVY BALLOT X and forgot that it’d be trashed.
On the double escaping thing, I thought of it earlier on and was careful to make sure it did the right thing, but then I transformed the code in such a way that that it ended up being read by the HTML parser again, and neglected to switch it to double escaping, which was rather careless of me. \u0026 can be more briefly written as & (saving one character) or \x26 (saving two). I suppose this bumps my 229 up to 235 (the first of the three ampersands will be parsed correctly unescaped), and brings closer the possibility that DOM methods rather than crazy HTML strings may be more suitable (I think it was ending up something like ten or fifteen characters cheaper this way, even with escaping the value this long way).
I’m not willing to use <xmp> because it leaves a plausible injection attack open. In fact, I’d probably exploit it with my very first TODO item, “fix <xmp>…</xmp> injection attack”!
I like the way you’re thinking on these things. Not many people think of things like the double encoding, and although I didn’t think of <xmp> this time, I’ve definitely thought of it before on other similar things (and used it for fun on hard-coded values, but rejected it if user input is involved).
----
Taking a tip from the original challenger’s solution, I’ve refined my not-using-serialised-HTML solution down to 209 characters, once you replace \u2718 with the actual character which HN eats. Here presented with the necessary wrapping in a data: URI for convenience (<body> now being necessary):
Thanks. And I'm glad xmp still lives in developers' minds.
But whoa, this is approach is really impressive. That "e" function for creation and appending elements is witty -- I wouldn't think it is possible to have such clean and terse JS element "factory" that in effect outperforms (in character count metric) literal HTML content.
(I must confess it took me a while to wrap my head around it, so if anyone is struggling like me, this is the above code unrolled:)
var Doc = document;
var FormElem;
var BodyElem;
var AppendElem =
(
aParentElem
// reference
,
aTagElemRet
// tag name for new child, will become child itself
,
aContent
// (text) content for new child
) =>
(
aParentElem.append(aTagElemRet = Doc.createElement(aTagElemRet))
// aTagElemRet tag name (string) becomes HTMLElement
// and is immediately appended at the end of reference element
,
// comma operator evaluates both sides, returns right
aContent && aTagElemRet.append(aContent)
// if third argument is truthy, put it into the new HTMLElement
,
aTagElemRet
// expression results in this so arrow function returns the new HTMLElement
);
var InputElem = AppendElem
(
FormElem = AppendElem
(
BodyElem = Doc.body, // = parent
"form" // = tagName
), // = parent
"input" //= tagName
// no content
);
var ListElem = AppendElem(
BodyElem, // = parent
"ol" // = tagName
);
FormElem.onsubmit =
pParamLI =>
// event handler gets SubmitEvent object as a sole argument
// but it is not needed, so parameter will be reassigned to
// newly created LI element
(
AppendElem(
pParamLI = AppendElem(
ListElem, // = parent
"li", // = tagName
InputElem.value + " " // = (text) content
), // = parent
"button", // = tagName
"×" // = content
).onclick = _ => pParamLI.remove(), !1
// inline handler attached to created button
// ClickEvent passed in _ is not used
// return !1 what evaluates to false
// inline handler returning false prevents default action
// in this case form submission
// button is in default type, so "submit" would be dispatched
// on its form otherwise
);
As you see, it’d be shorter as straight HTML without the “body must be empty, you can only write inside a <script> tag” requirement, as you could just drop the document.write("…") wrapping.