-
Notifications
You must be signed in to change notification settings - Fork 52
/
html.ex
74 lines (55 loc) · 1.81 KB
/
html.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
defmodule Kino.HTML do
@moduledoc ~S'''
A kino for rendering HTML content.
The HTML may include `<script>` tags with global JS to be executed.
In case you need to parameterize the HTML with dynamic values, write
a custom `Kino.JS` component.
## Examples
Kino.HTML.new("""
<h3>Look!</h3>
<p>I wrote this HTML from <strong>Kino</strong>!</p>
""")
Kino.HTML.new("""
<button id="button">Click</button>
<script>
const button = document.querySelector("#button");
button.addEventListener("click", (event) => {
button.textContent = "Clicked!"
});
</script>
""")
'''
use Kino.JS
@type t :: Kino.JS.t()
@doc """
Creates a new kino displaying the given HTML.
"""
@spec new(String.t()) :: t()
def new(html) when is_binary(html) do
Kino.JS.new(__MODULE__, html)
end
asset "main.js" do
"""
export function init(ctx, html) {
setInnerHTML(ctx.root, html);
}
function setInnerHTML(element, html) {
// By default setting inner HTML doesn't execute scripts, as
// noted in [1], however we can work around this by explicitly
// building the script element.
//
// [1]: https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML#security_considerations
element.innerHTML = html;
Array.from(element.querySelectorAll("script")).forEach((scriptEl) => {
const safeScriptEl = document.createElement("script");
Array.from(scriptEl.attributes).forEach((attr) => {
safeScriptEl.setAttribute(attr.name, attr.value)
});
const scriptText = document.createTextNode(scriptEl.innerHTML);
safeScriptEl.appendChild(scriptText);
scriptEl.parentNode.replaceChild(safeScriptEl, scriptEl);
});
}
"""
end
end