Narcissistic, Perfectly Modest

    matchdom

    5.10.8 • Public • Published

    matchdom -- merge data into DOM

    Write expressions in attributes or text nodes and have them merged with the help of filters for complex or custom cases.

    Features:

    • Securely traverses and mutates DOM attributes and nodes.
    • Super customizable with filters.
    • Comes with powerful filters: attr, magnet, repeat, join, and much more.
    • Source code is short, simple, and does not require compilation.
    • Nested expressions (since 1.6.0).
    • Can run without DOM when merging pure strings (since 2.1.0).
    • Test suite.
    • Supports legacy (window.matchdom), cjs, and esm styles

    Examples

    Run some code on here

    usage

    matchdom(node, data, filters?, scope?) -> node

    matchdom(string, data, filters?, scope?) -> value or string

    (since 5.0.0 if the whole string is an expression, it's the expression value that is returned).

    Given a DOM and placeholders like this:

    <div id="model" class="[myclass]">
    	<h[n]>Header</h[n]>
    	<span>[data.text]</span>
    </div>

    matchdom can be used like this to merge data:

    matchdom(model, {
    	n: 4,
    	myclass: "yes",
    	data: {
    		text: "test"
    	}
    }); // returns model

    resulting in:

    <div id="model" class="yes">
    	<h4>Header</h4>
    	<span>test</span>
    </div>

    The Node given to matchdom is mutated. Sometimes it is desirable to keep the model unmodified:

    var copy = matchdom(model.cloneNode(true), ...);
    

    filters

    Full control over how fields are replaced is possible using filters:

    <div id="model">
    	<span>Remove span if empty [text|magnet]</span>
    </div>
    matchdom(model, {some: "data"}, {
    	magnet: function(value, what) {
    		if (value == null) what.parent.remove();
    	}
    });

    The filter always receives the two first arguments where what is an object with the following properties:

    • data: the initial data object
    • scope: an object representing currently resolved state:
      • data: the current data object, takes precedence for finding values
      • path: the current path used to find the data from the initial data,
        a filter should find this to be always true:
        what.get(what.data, what.scope.path) == val
        This is a breaking change introduced in matchdom 3.0.0.
      • iskey: boolean, set when keys of an object are repeated and merged value is a key.
      • alias: scope.data[scope.alias] is the currently iterated data.
    • node: text node when expression was inside one
    • tag: boolean true when expression is inside a tag name (matchdom 4.3.0)
      in this case, expressions must be valid in lower case only.
    • attr: attribute name when expression was inside an attribute value
    • parent: node containing text node or attribute
    • expr: the parsed expression
    • filters: the object with custom filters
    • hits: the list of strings or expressions that will be concatenated
    • index: the current index of expression upon which the filter is called
    • val: last known non null value
    • cancel: if true, current expression is not merged

    and the following methods (which are useful to write filters that are independent of their position inside a text node or an attribute):

    • set(str): updates node or attr value
    • get(): returns node or attr value

    All these properties are mutable and changing them have an effect on what's being merged.

    In particular, changing what.attr will remove the original attribute and set a new attribute with that name.

    what itself is shared by filters of the same field, allowing filters to set flags picked up by other filters (like the mode flag set by html, text, and br filters).

    Filters can receive more string parameters by appending :param (once or multiple times) to the filter name, like this:

    <table>
    	<tr>
    		<td>[text|magnet:tr]</td>
    	</tr>
    </table>

    Multiple filters can be appended: [text|pre:me |post: him].

    Parameters values can be escaped using encodeURIComponent.

    A filter can itself change what.expr.filters, typically the repeat filter, being recursive, empties following filters.

    Several default filters are provided, see below.

    expressions

    A parsed expression has properties:

    • path (array of strings)
    • filters (array of {name, fn, params} objects where params is an array)
    • filter (index of current filter being applied in filters)

    and two methods:

    • clone()
    • toString()
    • get(data) returns the data accessed by expr.path

    When the last item of the path of an expression refers to an undefined value, the value is converted to null, so the expression is merged.

    When the path refers to an undefined value before the last item, the expression is not merged.

    It can be confusing when repeating an array, in which case either the repeat filter should be used first, or it should get an alias parameter to avoid merging undefined expressions that are actually meant to be repeated.

    Expressions can be modified by filters. See the repeat filter for the most complex code doing that.

    Expressions can be nested:

    <span>[val|or:[otherval]]</span>
    

    (see examples in tests).

    escaping

    The simplest way to write bracketed expressions without merging them is to use the or filter:

    [|or:%5Ba bracketed expressions%5D]
    

    booleans in attributes

    When merging only booleans in an attribute:

    • if boolean AND of all values is true, attribute value is "true" for data-attributes, or "" for other attributes
    • if boolean AND of all values is not true, attribute is removed

    bundled filters

    Examples can be found in tests.

    repeat, magnet, url filters provide most of the interesting features.

    repeat:selector:alias:step:offset:limit

    Repeats closest repeatable data over closest selected node, with optional alias parameter.

    Multiple repeat filters can be appended, so it is really easy to merge recursively rows and cells to form a table, see unit tests for examples:

    <table><tr>
    <td>[rows.cells.value|repeat:tr:cell|repeat]-[cell.unit]</td>
    </tr></table>

    If selector is prefixed or postfixed by one or several + signs, as many previous or next element siblings (or characters in text mode) are repeated:

    <div>
    	<hr>
    	<p>[sections.text|repeat:+p+]</p>
    	<br>
    </div>

    If selector is *, current node (or current expression in text mode) is selected:

    <div>
    	<p>[sections.text|repeat:*+]</p><br>
    </div>

    Without selector, current node (or the whole string in text mode) is selected.

    In text mode, the only bare selector allowed is *.

    • step parameter (an integer, defaults to 1) allows one to iterate by step, negative values are interpreted as iterating in reverse order
    • offset parameter adds an offset to the start of iteration (defaults 0)
    • limit parameter limits the number of total iterations (0 is no limit).

    To repeat over the keys of an object as a list of {key, val} items, append + to its path name:

    <table><tr>
    <td>[obj+.key|repeat:tr:pair]</td><td>[pair.val]</td>
    </tr></table>

    This used to be the keys filter in matchdom 2, and it is a breaking change in matchdom 3.

    In this case what.scope.path is the path to the value, however when it is actually a key being merged, what.scope.iskey is set to true.

    Caveat: won't work if there is actually a property obj+, in that case a warning will be emitted.

    Note that if root node is repeated, matchdom returns a fragment.

    magnet:selector

    Removes the closest node when current value is null or undefined, in which case the returned value is set to null.

    selector is optional and supports same syntax as in repeat filter. If not set, the parent node is removed, or expression is inside an attribute, the attribute is removed.

    bmagnet:selector

    Same as magnet, only it takes a boolean to decide, and does not print anything.

    Synonym of !!|magnet:sel|.

    In particular, it's useless to add a filter after bmagnet.

    url:name:selector

    The name parameter is optional as for attr:name (which is called by this filter).

    This filter builds a url attribute from a template and merges it into a target attribute.

    • replace target pathname by url pathname
    • merge target query with url query

    attr:name:selector

    The name parameter is optional for data-* attributes.

    The selector parameter is optional and selects an ancestor only when defined in a text node.

    Sometimes the template for an attribute is better kept in another attribute, so there is a filter just for that:

    <div id="model">
    	<img data-src="[url|attr]" />
    	<img something="[url|attr:src]" />
    	<a>test[url|attr:href]</a>
    	<div class="test"><p>test[myclass|attr:class:div]</p></div>
    </div>

    gives:

    <div id="model">
    	<img src="/my.png" />
    	<img src="/my.png" />
    	<a href="/my.png">test</a>
    	<div class="test my"><p>test</p></div>
    </div>

    It's possible to set an attribute with an expression in a text node.

    When targeting a class attribute, values are added using classList.add.

    The empty filter:str

    The empty filter, usually used as the last filter in an expression, unconditionally sets current value to the str parameter, or to the empty string if none given.

    or:str

    If value is null or undefined, merge the field with str.

    Can be useful also if undefined values are expected since they could be left unmerged (thus showing template expressions).

    br

    All strings are set into text nodes with newlines replaced by <br>.

    This filter is the default behavior.

    text

    The text filter removes default br behavior (newlines won't be replaced).

    html:selector

    The html filter creates DOM nodes out of string(s).

    The selector allows one to filter the result using querySelectorAll.

    fill

    Sets current node content to merged field.

    If it's used in an attribute, also removes the attribute containing the expression.

    Can be used with attr filter to set a value to an attribute and content at the same time.

    Because <[tag]> can't be parsed as DOM, use <a[tag|fill]> to define whole tag name.

    eq:str:yes:no

    Without parameters, returns str == value.

    Otherwise return yes or no, or does not change value if no is not passed.

    neq:str:yes:no

    Like eq but test is str != value.

    Not exactly eq:str|!|?:yes:no because ? has different behavior with missing parameters.

    not,ornull

    If value is falsey, replace it with null.

    padStart, padEnd :len:char

    Converts to string and calls String.prototype.padXxx(len, char).

    trim, trimStart, trimEnd, lower, upper, capitalize

    Converts to string and calls them.

    date:method:param

    Converts to date and calls Date.prototype[method](param).

    If no method is given, or method is not found, calls toLocaleString.

    join:pre:tag:post

    Joins an array with optional tag and characters before/after tag.

    Often useful with [list|join::br].

    split:sep:trim

    Splits a string with separator, and removes items equal to trim (typical use is to remove spaces).

    Example [str|split:%0A:|join:: - ].

    slice:begin:end

    Slices an array with optional end index. Works well with split and join filters.

    ?:yes:no

    If value is true, replace it with yes, if it is false, replace it with no, otherwise do not change the value.

    If no is not set, replace it with empty string. If yes is not set either, replace it with current variable name.

    This filter works well with not filter to make sure empty values are cast to boolean false.

    ! or !!

    The ! or !! operators.

    !?

    Like ? applied on !val.

    pre:str

    Prepend string if value is not null or not empty.

    post:str

    Append string if value is not null or not empty.

    gt:num, lt:num, gte:num, lte:num

    Comparisons, return booleans. If cannot be compared, returns value.

    battr

    Boolean attribute: if value is true, returns the current attribute name; else removes the current attribute.

    Hooks

    Special filter names (which cannot be parsed as legit names) provide hooks:

    • |
      hook before all filters have run (even if none where defined)

    • |
      hook after filter

    • ||
      hook after all filters have run (even if none where defined)

    This is available since version 4.2.0.

    Custom symbols

    matchdom.Symbols allows one to change open, close, path, append, param symbols.

    Install

    npm i matchdom

    DownloadsWeekly Downloads

    188

    Version

    5.10.8

    License

    MIT

    Unpacked Size

    66.9 kB

    Total Files

    5

    Last publish

    Collaborators

    • kapouer