Applying Variants

Episode 7: a variant is a list of modifications applied to elements the SDK must first find, and every step has a named failure

Guide~3 min read

Copy this guide and your current settings as a prompt for your AI agent.

Preview agent promptSee the guide, code and settings included when you copy.

Every episode so far ended at the same sentence: and then the variant is applied. This one opens that. A variant is not a page. It is a list of modifications, each pointed at an element the SDK has to find first, in a document that may have changed since the selector was recorded. Finding is where it gets interesting.

Watch: 3m 51s — Runtime Walkthrough, episode 7: what a variant is, finding the element, when it is not found, update, delete and execute, how the JS runs, stamping and revealing, and the failure map
On this page
  1. 01What a variant is
  2. 02Finding the element
  3. 03When it is not found
  4. 04Update
  5. 05Delete and execute
  6. 06How the JavaScript actually runs
  7. 07Stamping and revealing
  8. 08The failure map
  9. 09In short
  10. 10Where in the code

01What a variant is

A variant carries modifications keyed by element id. The frame carries elements, the selectors for those ids. The loop (applyVariantChanges in sdk-js/src/dom.ts) runs over the modifications, not the elements. That is deliberate: a modification pointing at an element id the frame no longer describes still gets caught and logged instead of silently doing nothing. An empty modification list is the original variant: it signals rendered and returns.

02Finding the element

Each element id carries a list of selectors, tried in order; first hit wins. A selector is stricter than you might expect (getElementUniversal in shared/universalSelector.ts): an XPath that matches zero nodes fails, and an XPath that matches more than one also fails. Exactly one, or it is not a match. Selectors can nest with and and or: an or returns the first that matches, an and requires all of them and returns the last.

03When it is not found

When no selector matches, the SDK logs element_not_found with the element id, every selector it tried, and the variant and experiment ids, then continues to the next modification. One missing element does not abandon the rest of the variant. This is the single most common defect in the field, and it is usually the page that changed, not the selector that was wrong.

04Update

Three modification types; update is the common one, and its rules are specific (applyUpdateModification in shared/dom.ts):

  • innerHTML wins over textContent if both are present.
  • textContent on an input button sets value, not text.
  • src is set for images.
  • Styles are converted, then applied with !important, but only where the value actually differs from what is already there.
  • Anything else in the attribute bag is logged as unknown_attributes, not silently dropped.

05Delete and execute

delete removes the node from its parent. execute_js is the escape hatch: arbitrary JavaScript for anything the other two cannot express. One subtlety: update and delete both signal that rendering is done. execute_js does not. It hands that responsibility to the variant’s own script, because only that script knows when it is finished.

06How the JavaScript actually runs

Not with eval. The SDK builds a <script> element, sets a nonce if your policy needs one, appends it to <head>, and immediately removes it (executeJs in shared/dom.ts). Two details earn their place: the variant’s code is wrapped with a leading newline, so a payload ending in a line comment cannot swallow the closing brackets; and a marker is set at the very end. If that marker is missing the script never parsed, and the SDK raises a SyntaxError instead of reporting success.

07Stamping and revealing

Every edited element gets stamped coframe-exp-id, set to the variant id. That stamp is what episode 5’s view tracking looks for, and what you look for in a live page to see whether a variant actually landed. Every path through this code calls variantRendered, which is the command the reveal in episode 3 is waiting on.

08The failure map

Name Cause
element_not_found The selector matched nothing
element_not_edited Found, but the edit did not take
no_modifications_for_element An element id with nothing to do
unknown_modification_type A payload this bundle does not understand
unknown_attributes An update carrying keys the applier ignores

Five names, five distinct causes, all in the monitoring stream, all searchable.

09In short

Find the element, exactly one or nothing. Apply one of three modification types. Stamp it. Signal rendered. When any of that fails, fail by name and keep going.

10Where in the code

File What to look for
sdk/sdk-js/src/dom.ts applyVariantChanges, editElement, querySelector nesting
sdk/shared/dom.ts applyUpdateModification, executeJs
sdk/shared/universalSelector.ts getElementUniversal

Related reading

Content and video lessons from Coframe documentation. Code examples are not executed on this page.

BACK TOHow Coframe Works

Documentation

Open Coframe App

Search documentation

Search all guides and references↑ ↓ Navigate · Esc Close