Media, animation and other events
Sound, video, animations and transitions all report what they are doing through events, and so do a few special parts of the page: slots, canvases and the security policy. This lesson covers the 33 events for them, builds a small player, and says exactly when the browser fires the ones you cannot trigger by hand.
Media elements and a sound to play
The audio and video elements play a file and report what happens to it. Their attributes are source (the file), controls (the browser's own buttons), preload (how much to fetch before the user presses play: none, metadata or auto), loop and muted. Their events follow the life of the file: it starts loading, it knows its length, it can play, it plays, it ends. Event attributes work as in the previous lesson: a function name, a short lambda, or a JavaScript string.
The examples of this lesson have no network, so they make their own sound. The function clip returns a WAV file as a data: address: a 54 byte header, written in base64, and a short square wave tone that is added 400 times, which makes three seconds of beeping. Every example sets the src of the audio element from a button, so you see the load events in their real order. Browsers only play sound after the user clicked something on the page, so the Play buttons are real clicks, not code that runs on load.
The life of a media file
This page logs the events of loading and playing. Press Load, then Play, and compare the log with the list below. The log is what Chrome produces; the order of neighbours such as suspend and loaded data can differ in other browsers.
import dom
mut lines := ""
mut count := 0 as i64
func say(what: str):
if count == 12: lines = "" count = 0 end
lines = lines + what + "\n"
count += 1
dom.SetText(dom.ById("log"), lines)
end
func clip(): str:
mut sound := "data:audio/wav;base64,UklGRu5dAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgASlVOSwIAAAAAAGRhdGHAXQAA"
repeat 400: sound += "oKCgoKCgoKCgoGBgYGBgYGBgYGCgoKCgoKCgoKCgYGBgYGBgYGBgYKCgoKCgoKCgoKBgYGBgYGBgYGBg" end
ret sound
end
func load_clip():
dom.SetAttr(dom.ById("sound"), "src", clip())
end
func play_clip():
_ := dom.Call0(dom.ById("sound"), "play")
end
page:
global: selector = "body" font family = "system-ui, sans-serif" margin = 24 end
audio: id = "sound" controls = true preload = "none"
load start = (): say("load start") end
suspend = (): say("suspend") end
loaded metadata = (): say("loaded metadata") end
loaded data = (): say("loaded data") end
can play = (): say("can play") end
can play through = (): say("can play through") end
play = (): say("play") end
playing = (): say("playing") end
pause = (): say("pause") end
ended = (): say("ended") end
end
box: display = flex gap = 8
button: click = load_clip content = "Load the sound" end
button: click = play_clip content = "Play" end
button: click = (): _ := dom.Call0(dom.ById("sound"), "pause") end content = "Pause" end
end
paragraph: id = "log" white space = "pre-wrap" content = "Events appear here." end
endload start: the browser begins to fetch the file.suspend: it stopped fetching on purpose. Withpreload = "none"it waits for the first play, and later it also stops when its buffer is full.loaded metadata: the length (and, for video, the size) is known. Readdurationhere.loaded data: the first moment can be shown.can play: enough data to start.can play through: enough to reach the end at the current speed.play: playing was requested.playing: it is really running.pauseandendedclose the story.
One thing deserves a name: play and playing are not the same. play fires the moment the button is pressed, even when no data has arrived. playing fires when sound or picture is moving, and again after every pause for buffering. A button that must show Pause should follow playing and pause, not the click.
Do not set src after the page asked for play() and expect the old sound to go on. Changing the source resets the element, and the browser fires emptied and starts the whole life cycle again.
A small custom player
The browser's controls are fine, but a page often wants its own. The example has a button, a progress bar and a volume slider, and every part reacts to an event instead of to the click that caused it. play and pause change the label of the button, so it is right even when something else pauses the sound. time update fires about four times a second and moves the bar with the share currentTime / duration. volume change writes the new volume next to the slider. The slider itself sets volume from its input event.
The state of the element is read with dom.GetNum and dom.GetBool (currentTime, duration, paused) and changed with dom.SetNum and dom.Call0, which calls methods such as play and pause.
import dom
func clip(): str:
mut sound := "data:audio/wav;base64,UklGRu5dAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgASlVOSwIAAAAAAGRhdGHAXQAA"
repeat 400: sound += "oKCgoKCgoKCgoGBgYGBgYGBgYGCgoKCgoKCgoKCgYGBgYGBgYGBgYKCgoKCgoKCgoKBgYGBgYGBgYGBg" end
ret sound
end
func play_pause():
sound := dom.ById("sound")
if dom.GetAttr(sound, "src") == "":
dom.SetAttr(sound, "src", clip())
end
if dom.GetBool(sound, "paused"):
_ := dom.Call0(sound, "play")
else:
_ := dom.Call0(sound, "pause")
end
end
func move_bar():
sound := dom.ById("sound")
now := dom.GetNum(sound, "currentTime")
dom.SetStyle(dom.ById("fill"), "width", now * 100.0 / dom.GetNum(sound, "duration") + "%")
dom.SetText(dom.ById("time"), dom.Call1(dom.Global("Math"), "floor", now) + " s of 3 s")
end
func set_volume():
dom.SetNum(dom.ById("sound"), "volume", dom.GetNum(dom.ById("level"), "valueAsNumber") / 100.0)
end
func show_volume():
percent := dom.Call1(dom.Global("Math"), "round", dom.GetNum(dom.ById("sound"), "volume") * 100.0)
dom.SetText(dom.ById("say"), "Volume " + percent + "%")
end
page:
global: selector = "body" font family = "system-ui, sans-serif" margin = 24 max width = 360 end
audio: id = "sound" preload = "none" play = (): dom.SetText(dom.ById("go"), "Pause") end
pause = (): dom.SetText(dom.ById("go"), "Play") end time update = move_bar volume change = show_volume
end
box: display = flex gap = 12 align items = center
button: id = "go" click = play_pause content = "Play" width = 70 end
box: flex = 1 height = 8 background = "#e2e8f0" border radius = 4
box: id = "fill" width = "0%" height = 8 background = "#7c3aed" border radius = 4 end
end
end
paragraph: id = "time" content = "0 s of 3 s" end
box: display = flex gap = 12 align items = center
input: id = "level" type = "range" min = 0 max = 100 value = 100 input = set_volume end
span: id = "say" content = "Volume 100%" end
end
endSeeking, speed and length
seeking fires when a jump to another time starts and seeked when it is finished. Setting currentTime causes both, and so does the user dragging the slider of the browser controls. rate change fires when playbackRate changes: 1 is normal, 0.5 is half speed. duration change fires when the length becomes known or changes, and emptied when the element is reset, as the Reset button does by calling load().
import dom
mut lines := ""
func say(what: str):
lines = lines + what + "\n"
dom.SetText(dom.ById("log"), lines)
end
func clip(): str:
mut sound := "data:audio/wav;base64,UklGRu5dAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgASlVOSwIAAAAAAGRhdGHAXQAA"
repeat 400: sound += "oKCgoKCgoKCgoGBgYGBgYGBgYGCgoKCgoKCgoKCgYGBgYGBgYGBgYKCgoKCgoKCgoKBgYGBgYGBgYGBg" end
ret sound
end
func load_clip():
dom.SetAttr(dom.ById("sound"), "src", clip())
end
func skip():
dom.SetNum(dom.ById("sound"), "currentTime", 2.0)
end
func set_speed(rate: f64):
dom.SetNum(dom.ById("sound"), "playbackRate", rate)
end
func restart():
_ := dom.Call0(dom.ById("sound"), "load")
end
page:
global: selector = "body" font family = "system-ui, sans-serif" margin = 24 end
audio: id = "sound" preload = "auto"
duration change = (): say("duration change: " + dom.GetNum(dom.ById("sound"), "duration") + " s") end
seeking = (): say("seeking") end seeked = (): say("seeked") end
rate change = (): say("rate change: " + dom.GetNum(dom.ById("sound"), "playbackRate")) end
emptied = (): say("emptied") end
end
box: display = flex gap = 8 flex wrap = wrap
button: click = load_clip content = "Load" end
button: click = skip content = "Jump to 2 s" end
button: click = (): set_speed(0.5) end content = "Half speed" end
button: click = (): set_speed(1.0) end content = "Normal speed" end
button: click = restart content = "Reset" end
end
paragraph: id = "log" white space = "pre-wrap" content = "Press Load first." end
endCaptions and cue change
A track element inside an audio or video element holds captions in the WebVTT format, a plain text file with time ranges. Its cue change event fires whenever a caption begins or ends. The text of the current one is in track.activeCues: the example reads the list with dom.GetProp, checks its length, and shows the first cue's text in a heading. The track needs default = true (or a script that sets its mode) or no cues become active. The page language accepts only ordinary addresses for the file, so the example gives the track a placeholder name and sets the real src, a data: address, from code when Play is pressed.
import dom
const WORDS := "data:text/vtt,WEBVTT%0A%0A00:00.000 --> 00:01.000%0ASalam%0A%0A00:01.000 --> 00:02.000%0Aeveryone%0A%0A00:02.000 --> 00:03.000%0Awelcome"
func show_cue():
cues := dom.GetProp(dom.GetProp(dom.ById("words"), "track"), "activeCues")
if dom.GetNum(cues, "length") > 0.0:
dom.SetText(dom.ById("line"), dom.GetStr(dom.ItemAt(cues, 0), "text"))
else:
dom.SetText(dom.ById("line"), "...")
end
end
func clip(): str:
mut sound := "data:audio/wav;base64,UklGRu5dAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgASlVOSwIAAAAAAGRhdGHAXQAA"
repeat 400: sound += "oKCgoKCgoKCgoGBgYGBgYGBgYGCgoKCgoKCgoKCgYGBgYGBgYGBgYKCgoKCgoKCgoKBgYGBgYGBgYGBg" end
ret sound
end
func play_captioned():
sound := dom.ById("sound")
dom.SetAttr(dom.ById("words"), "src", WORDS)
dom.SetAttr(sound, "src", clip())
_ := dom.Call0(sound, "play")
end
page:
global: selector = "body" font family = "system-ui, sans-serif" margin = 24 end
audio: id = "sound" controls = true
track: id = "words" kind = "captions" default = true cue change = show_cue source = "captions.vtt"
end
end
button: click = play_captioned content = "Play with captions" end
heading: size = 2 id = "line" content = "Press play." end
endAnimation events
A CSS animation moves an element by itself, and four events tell what stage it is at. animation start fires when it begins, animation iteration at the end of each round except the last, animation end after the last round, and animation cancel if it is cut off: for instance when the element is hidden with display: none. The animation here is defined in a style: element with @keyframes, and a class switches it on, three rounds of half a second each. So the log of an uninterrupted run is one start, two iterations and one end, and a cancelled run is a start and a cancel.
The end handler removes the class, which lets the button start it again. That is the usual job of animation end. An animation that never ends (infinite) never fires it.
import dom
mut lines := ""
func say(what: str):
lines = lines + what + "\n"
dom.SetText(dom.ById("log"), lines)
end
func run_ball():
lines = ""
dom.AddClass(dom.ById("ball"), "run")
end
func run_over():
dom.RemoveClass(dom.ById("ball"), "run")
say("animation end")
end
func restore():
dom.RemoveClass(dom.ById("ball"), "run")
dom.SetStyle(dom.ById("ball"), "display", "")
end
func cancel_ball():
dom.SetStyle(dom.ById("ball"), "display", "none")
dom.SetTimeout((): restore() end, 300)
end
page:
style: content = "@keyframes slide { to { transform: translateX(200px) } } .run { animation: slide 0.5s ease-in-out 3 }" end
global: selector = "body" font family = "system-ui, sans-serif" margin = 24 end
box: width = 260 padding = "10px 0"
box: id = "ball" width = 40 height = 40 border radius = "50%" background = "#e11d48"
animation start = (): say("animation start") end
animation iteration = (): say("animation iteration") end
animation end = run_over
animation cancel = (): say("animation cancel") end
end
end
box: display = flex gap = 8
button: click = run_ball content = "Run 3 times" end
button: click = cancel_ball content = "Cancel it" end
end
paragraph: id = "log" white space = "pre-wrap" content = "" end
endTransition events
A transition is the smooth change of a property after it was set, and it has four events. transition run fires when the transition is created, before its delay. transition start fires when the delay is over and the value begins to move. transition end fires when it arrives. transition cancel fires when something interrupts it. All four carry propertyName, so a transition of two properties fires each event twice. The example gives the move a delay of 0.3 seconds, so run and start are visible apart, and the second button flips the class twice at once to cut the transition off.
There is one trap that is not in your code. In our tests Chromium, the engine of Chrome and Edge, read transition end from an HTML attribute but not transition run, transition start or transition cancel; Firefox reads all four. Writing them as attributes compiles and works in Firefox, and silently does nothing in Chrome. The reliable way is dom.OnEventE(element, "transitionrun", ...), which the example uses for those three, and it also shows the plain attribute for transition end.
import dom
mut lines := ""
mut wired := false
func record(kind: str, e: i64):
lines = lines + kind + ": " + dom.GetStr(e, "propertyName") + "\n"
dom.SetText(dom.ById("log"), lines)
end
func transition_ended(e: i64):
record("end", e)
end
func wire_events():
if not wired:
wired = true
chip := dom.ById("chip")
dom.OnEventE(chip, "transitionrun", (e: i64): record("run", e) end)
dom.OnEventE(chip, "transitionstart", (e: i64): record("start", e) end)
dom.OnEventE(chip, "transitioncancel", (e: i64): record("cancel", e) end)
end
end
func move_chip():
wire_events()
lines = ""
dom.ToggleClass(dom.ById("chip"), "moved")
end
func move_and_undo():
move_chip()
dom.SetTimeout((): dom.ToggleClass(dom.ById("chip"), "moved") end, 150)
end
page:
style: content = ".moved { transform: translateX(180px) }" end
global: selector = "body" font family = "system-ui, sans-serif" margin = 24 end
box: width = 240 padding = "10px 0"
box: id = "chip" width = 50 height = 40 border radius = 8 background = "#2563eb"
transition = "transform 0.4s 0.3s" transition end = transition_ended
end
end
box: display = flex gap = 8
button: click = move_chip content = "Move it" end
button: click = move_and_undo content = "Move and undo" end
end
paragraph: id = "log" white space = "pre-wrap" content = "" end
endSlots and slot change
A template with shadow root mode = "open" turns its parent into a shadow host: the content of the template becomes a separate little tree, and the original children of the host are shown wherever a slot is. slot change fires on the slot when the nodes that fill it change, so it is how a component notices that someone added a child. assignedNodes(), called with dom.Call0, lists them. Page styles do not reach inside a shadow tree, which is why the example puts the colours on the host.
import dom
mut members := 0 as i64
func slot_changed(e: i64):
nodes := dom.Call0(dom.EventTarget(e), "assignedNodes")
dom.SetText(dom.ById("log"), "slot change: " + dom.GetNum(nodes, "length") + " in the slot")
end
func add_member():
members += 1
person := dom.Create("p")
dom.SetText(person, "Member " + members)
dom.Append(dom.ById("team"), person)
end
page:
global: selector = "body" font family = "system-ui, sans-serif" margin = 24 end
box: id = "team" padding = "4px 16px" background = "#f5f3ff" border = "2px solid #7c3aed" border radius = 8
template: shadow root mode = "open"
heading: size = 3 content = "Team" end
slot: slot change = slot_changed end
end
end
button: click = add_member content = "Add a member" margin top = 12 end
paragraph: id = "log" content = "Nothing in the slot yet." end
endSecurity policy violations
A Content Security Policy is a list of things a page promises not to do, set by a meta element with http equiv = "Content-Security-Policy" or by a header. When the page breaks its own promise the browser blocks the act and fires security policy violation, with the blocked address in blockedURI and the rule in violatedDirective. The example forbids style attributes. Setting one with dom.SetAttr(el, "style", ...) is blocked and reported on the element itself, while dom.SetStyle works because it changes the style object, not the attribute. Some violations, such as a blocked image, are reported on the document and not on the element.
import dom
func blocked(e: i64):
what := dom.GetStr(e, "blockedURI")
rule := dom.GetStr(e, "violatedDirective")
dom.SetText(dom.ById("log"), "Blocked: " + what + " (" + rule + ")")
end
func by_attribute():
dom.SetAttr(dom.ById("card"), "style", "background: gold")
end
func by_property():
dom.SetStyle(dom.ById("card"), "background", "lightgreen")
dom.SetText(dom.ById("log"), "SetStyle worked.")
end
page:
meta: http equiv = "Content-Security-Policy" content = "style-src-attr 'none'" end
global: selector = "body" font family = "system-ui, sans-serif" margin = 24 end
box: id = "card" security policy violation = blocked padding = 20 border radius = 8 background = "#e2e8f0"
content = "This card's style attribute is forbidden by the page's policy."
end
box: display = flex gap = 8 margin top = 12
button: click = by_attribute content = "Set the style attribute" end
button: click = by_property content = "Use SetStyle" end
end
paragraph: id = "log" content = "No violation yet." end
endEvents you cannot trigger here
A few events need a network that fails or a graphics system that breaks, so no example can show them. This is when the browser fires them.
stalled: it wants more data and none has come for a few seconds, for example on a slow mobile connection. It may recover on its own.waiting: playback stopped because the buffer ran dry.playingfollows when data arrives again. Show a spinner between them.progress: the browser downloaded another chunk of a remote file. A shortdata:address does not produce it.context lostandcontext restored: a 2D canvas loses its memory when the graphics driver resets or the system switches graphics cards, and gets an empty drawing back when it recovers. Everything on it has to be drawn again incontext restored. A page cannot cause it.
Reference: every event of this lesson
All of these are global attributes, but each one only fires on the elements named in its description. The examples are compiled by the checker; where an example needs a media file it uses an address that does not exist, so the event is described and not run. Three events of this lesson end in end or ended: ended, animation end and transition end. In Persian their names start with اتمام and not پایان, because پایان on its own would end the element.
Loading
| Attribute | When it fires | Example |
|---|---|---|
load startشروع بارگذاری onloadstart | Fires on an audio or video element when the browser starts to load its file, right after src is set or load() is called. It is the first event in the life of a media file. | audio: controls = true source = "/sounds/bell.mp3" load start = (): dom.SetText(dom.ById("out"), "Loading started") end end
paragraph: id = "out" content = "-" end |
progressپیشرفت onprogress | Fires every so often while the browser is downloading the file. Read buffered to see which time ranges are already there. Do not confuse it with time update, which is about playing. | audio: controls = true source = "/sounds/bell.mp3" progress = (): dom.SetText(dom.ById("out"), "More data downloaded") end end
paragraph: id = "out" content = "-" end |
suspendتعلیق onsuspend | Fires when the browser stops downloading on purpose and without an error: the buffer is full, or preload = none says to wait for the user. It is not a problem; downloading starts again when needed. | audio: controls = true source = "/sounds/bell.mp3" suspend = (): dom.SetText(dom.ById("out"), "Download paused") end end
paragraph: id = "out" content = "-" end |
stalledمتوقف شده onstalled | Fires when the browser wants more data but none has come for some seconds, for example when the connection is bad. It may recover by itself. You cannot cause it without a slow network, so a page usually only shows a hint when it fires. | audio: controls = true source = "/sounds/bell.mp3" stalled = (): dom.SetText(dom.ById("out"), "The data stopped coming") end end
paragraph: id = "out" content = "-" end |
loaded metadataبارگذاری ابرداده onloadedmetadata | Fires when the browser knows the duration and, for video, the size, but may not have any sound or picture yet. Read duration here, for example to size a progress bar. | audio: controls = true source = "/sounds/bell.mp3" loaded metadata = (): dom.SetText(dom.ById("out"), "Duration is known") end end
paragraph: id = "out" content = "-" end |
duration changeتغییر مدت ondurationchange | Fires when duration changes: when metadata arrives (from not a number to a real length), when the source changes, and for streams whose length grows. | audio: controls = true source = "/sounds/bell.mp3" duration change = (): dom.SetText(dom.ById("out"), "The length changed") end end
paragraph: id = "out" content = "-" end |
loaded dataبارگذاری داده onloadeddata | Fires when the first moment of the media can be shown, the first video frame or the first sound. It comes after loaded metadata. | audio: controls = true source = "/sounds/bell.mp3" loaded data = (): dom.SetText(dom.ById("out"), "The first data arrived") end end
paragraph: id = "out" content = "-" end |
can playقابل پخش oncanplay | Fires when enough data has arrived to start playing from the current position, although more may have to be loaded while it plays. It is the moment to enable a Play button. | audio: controls = true source = "/sounds/bell.mp3" can play = (): dom.SetText(dom.ById("out"), "Ready to play") end end
paragraph: id = "out" content = "-" end |
can play throughقابل پخش کامل oncanplaythrough | Fires when the browser estimates that, at the current download speed, it can play to the end without stopping to buffer. For a short local file it comes right after can play. | audio: controls = true source = "/sounds/bell.mp3" can play through = (): dom.SetText(dom.ById("out"), "Can play to the end") end end
paragraph: id = "out" content = "-" end |
emptiedخالی شدن onemptied | Fires when the media is reset to its start, because load() was called or the source changed after something had loaded. The old duration and position are gone. | audio: controls = true source = "/sounds/bell.mp3" emptied = (): dom.SetText(dom.ById("out"), "The media was reset") end end
paragraph: id = "out" content = "-" end |
Playing
| Attribute | When it fires | Example |
|---|---|---|
playپخش onplay | Fires when playback is asked for: the user pressed play or the code called play(). Data may not have arrived yet, so sound can start a moment later; playing says when it really does. | audio: controls = true source = "/sounds/bell.mp3" play = (): dom.SetText(dom.ById("out"), "Play was requested") end end
paragraph: id = "out" content = "-" end |
playingدر حال پخش onplaying | Fires when playback is really running, after play and also after a pause for buffering ended (waiting). Use it, not play, for a button that shows Pause. | audio: controls = true source = "/sounds/bell.mp3" playing = (): dom.SetText(dom.ById("out"), "It is playing") end end
paragraph: id = "out" content = "-" end |
pauseمکث onpause | Fires when playback is paused, by the user, by pause(), or by the browser just before ended. A paused element keeps its position. | audio: controls = true source = "/sounds/bell.mp3" pause = (): dom.SetText(dom.ById("out"), "Paused") end end
paragraph: id = "out" content = "-" end |
endedاتمام پخش onended | Fires when playback reaches the end and the element does not loop. A pause event comes just before it. Use it to show a Play again button or start the next track. | audio: controls = true source = "/sounds/bell.mp3" ended = (): dom.SetText(dom.ById("out"), "Playback finished") end end
paragraph: id = "out" content = "-" end |
time updateبهروزرسانی زمان ontimeupdate | Fires when currentTime changes, about four times a second while playing and also after a jump. It is the event that moves a progress bar. | audio: controls = true source = "/sounds/bell.mp3" time update = (): dom.SetText(dom.ById("out"), "The time moved") end end
paragraph: id = "out" content = "-" end |
seekingدر حال جابجایی onseeking | Fires when a jump to another time begins, because the user dragged the slider or the code set currentTime. The picture may not be there yet. | audio: controls = true source = "/sounds/bell.mp3" seeking = (): dom.SetText(dom.ById("out"), "Seeking") end end
paragraph: id = "out" content = "-" end |
seekedجابجا شده onseeked | Fires when the jump has finished and the media can play from the new time. It is the partner of seeking. | audio: controls = true source = "/sounds/bell.mp3" seeked = (): dom.SetText(dom.ById("out"), "The jump is done") end end
paragraph: id = "out" content = "-" end |
waitingدر انتظار onwaiting | Fires when playback stops because the next data has not arrived, and will go on by itself when it does; playing follows. Show a spinner between the two. | audio: controls = true source = "/sounds/bell.mp3" waiting = (): dom.SetText(dom.ById("out"), "Waiting for data") end end
paragraph: id = "out" content = "-" end |
volume changeتغییر صدا onvolumechange | Fires when volume or muted changes. The value is from 0 (silent) to 1 (full); read it to update a slider or an icon. | audio: controls = true source = "/sounds/bell.mp3" volume change = (): dom.SetText(dom.ById("out"), "The volume changed") end end
paragraph: id = "out" content = "-" end |
rate changeتغییر سرعت onratechange | Fires when playbackRate or defaultPlaybackRate changes. 1 is normal speed, 0.5 half speed, 2 double speed. | audio: controls = true source = "/sounds/bell.mp3" rate change = (): dom.SetText(dom.ById("out"), "The speed changed") end end
paragraph: id = "out" content = "-" end |
cue changeتغییر نشانه oncuechange | Fires on a track element when the set of active cues changes: a caption starts or ends. Read track.activeCues to get the text. The track must be showing or hidden, which default = true on a captions track does. | audio: controls = true source = "/sounds/talk.mp3"
track: kind = "captions" source = "/captions/talk.vtt" default = true cue change = (): dom.SetText(dom.ById("out"), "A caption changed") end end
end
paragraph: id = "out" content = "-" end |
Animations and transitions
| Attribute | When it fires | Example |
|---|---|---|
animation startشروع پویانمایی onanimationstart | Fires on an element when a CSS animation starts, after any animation-delay. event.animationName says which animation. It does not fire when the animation has a duration of zero. | box: animation = "spin 2s linear 3" animation start = (): dom.SetText(dom.ById("out"), "The animation started") end content = "Spin" padding = 20 end
paragraph: id = "out" content = "-" end |
animation iterationتکرار پویانمایی onanimationiteration | Fires at the end of each round of an animation that repeats, except the last one (that fires animation end). An animation that runs three times fires it twice. | box: animation = "spin 2s linear 3" animation iteration = (): dom.SetText(dom.ById("out"), "Another round") end content = "Spin" padding = 20 end
paragraph: id = "out" content = "-" end |
animation endاتمام پویانمایی onanimationend | Fires when a CSS animation has finished all its rounds. An animation that repeats forever (infinite) never fires it. Use it to remove a class that started the animation. | box: animation = "fade 1s" animation end = (): dom.SetText(dom.ById("out"), "The animation ended") end content = "Fade" padding = 20 end
paragraph: id = "out" content = "-" end |
animation cancelلغو پویانمایی onanimationcancel | Fires when a running animation is cut short without finishing: its element is hidden with display: none, removed from the page, or the animation property is taken away. No animation end follows. | box: animation = "spin 2s linear infinite" animation cancel = (): dom.SetText(dom.ById("out"), "The animation was cancelled") end content = "Spin" padding = 20 end
paragraph: id = "out" content = "-" end |
transition runاجرای گذار ontransitionrun | Fires when a CSS transition is created, before its delay. event.propertyName says which property moves. In our tests Chromium (the engine of Chrome and Edge) did not read this one as an HTML attribute, while Firefox did; attach it with dom.OnEventE(el, "transitionrun", ...) to be safe. | box: transition = "transform 0.4s 0.3s" hover transform = "scale(1.2)" transition run = (): dom.SetText(dom.ById("out"), "The transition was created") end content = "Hover me" padding = 20 end
paragraph: id = "out" content = "-" end |
transition startشروع گذار ontransitionstart | Fires when the transition really begins to move, after its delay is over. Without a delay it comes right after transition run. In our tests Chromium (the engine of Chrome and Edge) did not read it as an HTML attribute, while Firefox did; attach it with dom.OnEventE(el, "transitionstart", ...). | box: transition = "transform 0.4s 0.3s" hover transform = "scale(1.2)" transition start = (): dom.SetText(dom.ById("out"), "The movement began") end content = "Hover me" padding = 20 end
paragraph: id = "out" content = "-" end |
transition endاتمام گذار ontransitionend | Fires when a transition has finished moving, once for every property that changed. event.propertyName tells them apart. It does not fire if the transition was cut short (that is transition cancel). Browsers read it as an attribute everywhere. | box: transition = "transform 0.4s" hover transform = "scale(1.2)" transition end = (): dom.SetText(dom.ById("out"), "The transition ended") end content = "Hover me" padding = 20 end
paragraph: id = "out" content = "-" end |
transition cancelلغو گذار ontransitioncancel | Fires when a transition is interrupted before it ends, for example the mouse leaves during a hover transition and the value starts to go back, or the element is hidden. In our tests Chromium (the engine of Chrome and Edge) did not read it as an HTML attribute, while Firefox did; attach it with dom.OnEventE(el, "transitioncancel", ...). | box: transition = "transform 0.4s 0.3s" hover transform = "scale(1.2)" transition cancel = (): dom.SetText(dom.ById("out"), "The transition was interrupted") end content = "Hover me" padding = 20 end
paragraph: id = "out" content = "-" end |
Slots, canvas and security
| Attribute | When it fires | Example |
|---|---|---|
context lostاز دست رفتن زمینه oncontextlost | Fires on a canvas when the browser loses the memory behind its 2D drawing context, for example after a graphics driver reset or when the GPU is switched. Everything drawn is gone. It cannot be provoked from a page; browsers fire it only when the graphics system really fails. (A WebGL canvas has its own webglcontextlost.) | canvas: id = "pad" width = 200 height = 100 context lost = (): dom.SetText(dom.ById("out"), "The drawing was lost") end end
paragraph: id = "out" content = "-" end |
context restoredبازیابی زمینه oncontextrestored | Fires on a canvas after a lost context became usable again, with an empty drawing. Draw everything again here. Like context lost it only happens when the graphics system recovers from a failure. | canvas: id = "pad" width = 200 height = 100 context restored = (): dom.SetText(dom.ById("out"), "Draw it again") end end
paragraph: id = "out" content = "-" end |
security policy violationنقض سیاست امنیتی onsecuritypolicyviolation | Fires when the page breaks its own Content Security Policy, the rules set by a meta element or a header, for example by writing a style attribute when style-src-attr forbids it. The event holds blockedURI and violatedDirective. It bubbles from the element to the document, but a blocked file such as an image is reported on the document only. | box: security policy violation = (): dom.SetText(dom.ById("out"), "The policy blocked something") end content = "Guarded by a policy" end
paragraph: id = "out" content = "-" end |
slot changeتغییر شکاف onslotchange | Fires on a slot element inside a shadow tree when the nodes that fill the slot change: one was added, removed or moved. assignedNodes() lists the current ones. The shadow tree comes from a template with shadow root mode. | box: id = "team"
template: shadow root mode = "open"
slot: slot change = (): dom.SetText(dom.ById("out"), "The slot content changed") end end
end
end
paragraph: id = "out" content = "-" end |
Try it yourself
- Add a button to the player that skips 1 second ahead by setting
currentTime, and show in the log thatseekingandseekedfire. - Make a button that changes the speed to 2, and a label that follows
rate change. - Run the transition example with a transition of one property only, and compare the log with the two property version.
- Add a second slot with a name to the slot example, and add members to it with the
slotattribute.