# vfunc.js — full reference for LLMs (v1.2.0) > Generated by build/llms.mjs from llms.txt, the website pages (site/content/en), layer1/types/*.d.ts and layer2/ai/en/components.md. Do not edit. > build/llms.mjs가 생성합니다. 직접 고치지 마세요. 한국어 문서는 사이트(site/content/ko)에 있습니다. --- # Part 1 — Summary and rules (llms.txt) > vfunc.js is a no-build vanilla JavaScript UI library (Apache-2.0). No virtual DOM: components render with innerHTML, events use delegation, and elements are reached through `data-ref`. Idea: keep your HTML as it is and use vfunc only where you need control. One global `vf`. For vfunc.js 1.0. The full reference is llms-full.txt (this file + the website guide and API reference + the type declarations); this file keeps only what is needed to write correct code. Korean: llms.ko.txt. ## Load - Script tag: `` → global `vf`. Pin the exact version and add SRI in production; the integrity value differs per version and file (this one is 1.2.0 vfunc.min.js). `dist/vfunc.js` is the development build with warnings. - ES module: `import vf from 'vfunc'` or `import { vfunc, html, router } from 'vfunc'`; a copied file works the same (`import vf from './lib/vfunc.esm.min.js'`). The ES modules never touch `window.vf`. - IE11 / Edge IE mode: `dist/vfunc.legacy.min.js` (same API; includes only a Promise polyfill). The app code must be ES5: no arrow functions, const/let, template literals or classes; use `vf.tpl('{name}', data)` instead of vf.html template literals. Polyfill anything else the app needs (Object.assign, fetch, …) yourself. Load pattern: ` ``` ```html
``` ```js // app.js const hello = vf.vfunc({ state: { name: 'vfunc' }, render: (s) => vf.html`

Hello, ${s.name}!

`, delegates: [{ selector: '[data-action="rename"]', eventType: 'input', onEvent: (e) => { e.sender.name = e.target.value; } }] }); hello.mount('#app'); ``` - Develop with `vfunc.js` (the development build): it explains mistakes in the console. In production use `vfunc.min.js` with SRI, as above. - Use exact versions in URLs (`vfunc@1.2.0`). Ranges such as `vfunc@1` are cached by the CDN and pick up new versions late. - Open pages through a static server, not `file://`: `python -m http.server 8080`. ### As an ES module ```js import vf from 'vfunc'; // npm i vfunc import { vfunc, html, router } from 'vfunc'; // named imports work too ``` The ES modules never create a global `window.vf`. Bundlers pick the minified file without warnings for production builds (the `production` condition in `exports`). ### Start from the starter A copyable app with a router, shared state, Korean/English, themes and release tools. ```bash npx degit vidkid-indy/vfunc/layer1/starter my-app cd my-app python -m http.server 8080 ``` | Path | What | |---|---| | `app.js` | messages, layout, router, update plugin — one route per new screen | | `pages/*.js` | one `(ctx, router) => component` function per screen | | `store.js` · `api.js` | shared state and the functions that change it · every server call | | `locales/{en,ko}.json` | messages | | `styles/tokens.css` | design tokens (derived from `design/DESIGN.md`) | | `AGENTS.md` | rules read by AI tools (fill in section 0) | | `tools/release.mjs`, `deploy/` | version-folder releases and server cache settings | ### Read next - [Core concepts](guide.md): components, state, events, existing HTML, safe HTML - [SPA](spa.md): router, store, i18n - [Examples](examples.md): 21 of them, each with a source link ## Core concepts ### Components `vf.vfunc(options)` creates a component, with or without `new`. ```js const box = vf.vfunc({ tag: 'section', // root element, default 'div' state: { open: false }, // state render: (s) => vf.html` ${s.open ? vf.html`

More text

` : ''}`, methods: { close() { this.open = false; } }, delegates: [{ selector: '[data-action="toggle"]', eventType: 'click', onEvent: (e) => { e.sender.open = !e.sender.open; } }] }); box.mount('#app'); ``` - **State**: `box.open` is `box.state.open`. Assigning it or calling `setState({…})` schedules a render; changes in the same tick render once. - **Methods** are bound to the instance: call `box.close()`, or pass it around as a callback. - **Elements**: `data-ref="body"` → `box.refs.body`, `id="save"` → `box.ids.save`. Only inside the component's root; find elements outside it with `vf.$('#id')`. ### Events | Kind | Use for | |---|---| | `delegates: [{ selector, eventType, onEvent }]` | one listener on the root, matched with `closest(selector)` inside the root — one for every row of a list | | `events: [{ id, eventType, onEvent }]` | directly on one element (scroll, focus) | The event object is `{ sender, event, eventType, target, id, data }`. **Hang selectors on `data-action`, `data-ref` or `id` only.** Selectors on CSS classes break when the design changes. ### Existing HTML — `vf.attach` The core idea of vfunc: keep the publisher's HTML and add behaviour. ```js // Without render: adopt the element as it is (markup, typed values and styles stay) vf.attach('#search', { delegates: [{ selector: '[data-action="search"]', eventType: 'submit', onEvent: (e) => { e.event.preventDefault(); run(vf.form.values(e.sender.$node)); } }] }); // With render: the element stays, render fills its inside (areas that change with data) const results = vf.attach('#results', { state: { items: [] }, render: (s) => vf.html`…` }); ``` ### Safe HTML — `vf.html` `vf.html` looks at **where** each value lands. | Position | Handling | |---|---| | element content `

${x}

` | HTML-escaped; arrays joined; `vf.html` results inserted as markup | | quoted attribute `title="${x}"` | escaped; `false`/`null` empty, but `aria-*`/`data-*` keep `"true"`/`"false"` | | URL attribute `href`, `src`, `action` … | escaped + `vf.safeUrl` (`javascript:` refused) | | inside a tag `