Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

JS: DOM and i18n helpers

DOM and i18n helpers

The browser is an operating system, and its DOM is where a GUI gclass draws. These helpers build the elements and keep the untrusted values safe.

Source code: src/helpers.js


Build an element

createElement2()

Builds an HTML element from a description that is a plain array. It is the function that a GUI gclass uses to draw.

createElement2(description, translate_fn)

Parameters

KeyTypeDescription
descriptionarrayThe element, as [tag, attrs, content, events].
translate_fnfunctionThe translation function. The attribute i18n uses it.

Returns

The element that the function built.

The four slots

SlotTypeDescription
tagstringThe name of the element.
attrsobjectThe attributes. style accepts a string or an object. i18n gives the content through translate_fn.
contentstring or arrayThe text, one child, or a list of children.
eventsobjectThe listeners, as {event: function}.
// Data in the third slot: a Text node
['span', {class: 'HOST_NAME'}, document.createTextNode(host)]

// Data in a list of children (gobj-js 7.26.2+): a string is text
['p', {}, [['b', {}, 'Host: '], host]]
['div', { class: 'window-top', style: 'border-bottom: 1px solid black;' },
    [
        ['span', {}, 'title'],
        ['button', {}, 'Close', {
            click: (e) => {
                e.target.closest('.window').remove();
            }
        }]
    ]
]

The style attribute accepts both forms:

['span', {style: {position: 'absolute'} }, 'title']

html_markup(html)

Marks a string as markup for createElement2(), said out loud: the string is the caller’s own (an icon, an svg), never data. It works in the third slot and in a list of children, in any mode, and keeps every node of the string (since gobj-js 7.26.3).

['span', {class: 'icon'}, html_markup('<i class="yi-copy"></i>')]
['div', {}, [['b', {}, 'Saved'], html_markup('<i class="yi-check"></i>')]]

Turned into a string (String(m), a template, +), the value gives its html back, so code that still builds innerHTML keeps working. The string that comes out is a plain string again, and it is not marked: never add data to it. In createElement2() with set_strings_are_text(true) that string is text, and it is logged when it starts with <.

$el.innerHTML = `${html_markup('<i class="yi-check"></i>')}`;  // works
['span', {}, `${html_markup('<b>')}${name}`]                   // text, and logged

is_html_markup(v)

Tells whether v is a value that html_markup() returned.

is_html_markup(html_markup('<b>x</b>'));   // true
is_html_markup('<b>x</b>');                // false

set_strings_are_text(on)

Sets how createElement2() reads a plain string that starts with <. With false (the default) the string is markup, as it always was. With true it is text, and html_markup() is the only way to insert markup. In that mode a string that starts with < is logged once with a warning: it is a deliberate markup that was not migrated, or a datum that used to run as HTML.

An app turns the mode on at startup, after its own code and the libraries it mounts pass all their markup through html_markup(). Since 7.26.10 gobj-ui passes its markup that way, and the check node node_modules/@yuneta/gobj-ui/scripts/check-markup-strings.mjs src (published with gobj-ui 7.26.11+) finds the literal cases in an app.

import {set_strings_are_text} from "@yuneta/gobj-js";
set_strings_are_text(true);
createElement2(['span', {}, '<img src=x onerror=alert(1)>']);  // text, logged once

strings_are_text()

Returns the mode that set_strings_are_text() set.

if(!strings_are_text()) {
    log_warning("this app still reads '<' strings as markup");
}

createOneHtml(htmlString)

Builds one element from a string of HTML.

parseSVG(string)

Builds an SVG element from a string of SVG code.

getPositionRelativeToBody(element)

Gives the position of an element against the body of the document. A popover or a menu that must stay next to its button uses it.


Untrusted values

escapeHtml(str)

Escapes a value for an HTML context. Use it every time that a string from a user or from a server goes into a template that becomes innerHTML.

safeSrc(url)

Validates a URL for the src attribute of an image or a media element. It refuses the javascript: and data: schemes, which carry a cross-site script. It gives an empty string back for every scheme that it refuses.


Language

refresh_language()

Translates a DOM tree again, after the application changes its language.

refresh_language(element, t)

Parameters

KeyTypeDescription
elementElementThe root of the tree. With an empty value the function takes the whole document.
tfunctionThe translation function, such as the t of i18next.

What the function translates

It reads four attributes, and each one carries the key of its own text.

AttributeWhat it translates
data-i18nThe first text node of the element.
data-i18n-titleThe title attribute, which is the tooltip.
data-i18n-aria-labelThe aria-label attribute, which a screen reader reads.
data-i18n-placeholderThe placeholder attribute of an input or a text area.