Quick Start

A SmarkForm form can be created by following a few simple steps:

This section is meant to be educational. If you are eager to get your hands dirty, ๐Ÿ go straight to the Examples section, download the one you like the most, and start editing to see what happens.

๐Ÿ“– Table of Contents

Basic setup

Create an HTML document

<!DOCTYPE html>
<html>
  <head>
    <title>My First SmarkForm Form</title>
  </head>
  <body>
    <h1>My First SmarkForm Form</h1>
  </body>
</html>

Alternatively, you can just pick our Boilerplate file so that you can miss the third and fourth steps of this guide too and ๐Ÿš€ go stright to the nitty-gritty.

Include SmarkForm Library

Next, load SmarkForm to your document.

The easiest way is using a CDN:

<script src="https://cdn.jsdelivr.net/npm/smarkform@0.8.6/dist/SmarkForm.umd.js"></script>

Alternatively you can use a local copy of SmarkForm if you donโ€™t want to rely on external sources like a CDN.

๐Ÿ‘‰ You can get and include SmarkForm in your code in a lot of different ways.

You can also add an additional <script> tag for the few JS code we will need to write.

Our complete layout may look as follows:

<!DOCTYPE html>
<html>
  <head>
    <title>My First SmarkForm Form</title>
    <script src="https://cdn.jsdelivr.net/npm/smarkform@0.8.6/dist/SmarkForm.umd.js"></script>
  </head>
  <body>
    <h1>My First SmarkForm Form</h1>
    <!-- Your form markup goes here -->
    <script>
      /* Your JS code goes here */
    </script>
  </body>
</html>

Create a simple HTML form

Start by writing the form markup in plain HTML. For example, letโ€™s create a simple form like the following:

๐Ÿ”—
๐Ÿ—’๏ธ HTML
๐Ÿ‘๏ธ Preview
<div id="myForm">
    <p>
        <label for="nameField">Name:</label>
        <input type="text" id="nameField" name="name">
    </p>
    <p>
        <label for="emailField">Email:</label>
        <input type="email" id="emailField" name="email">
    </p>
    <p>
        <button>โŒ Clear</button>
        <button>๐Ÿ’พ Submit</button>
    </p>
</div>

You may notice that there is no <form> tag. Just a regular <div>, in this example with an Id that we will use to capture the node to enhance it as a SmarkForm instance.

You can use a <form> tag instead if you like, but it is not actually necessary.

Initialize the Form

In your JavaScript tag, create a new instance of the SmarkForm class and pass the DOM node of the form container as parameter:

๐Ÿ”—
๐Ÿ—’๏ธ HTML
โš™๏ธ JS
๐Ÿ‘๏ธ Preview
<div id="myForm">
    <p>
        <label for="nameField">Name:</label>
        <input type="text" id="nameField" name="name">
    </p>
    <p>
        <label for="emailField">Email:</label>
        <input type="email" id="emailField" name="email">
    </p>
    <p>
        <button>โŒ Clear</button>
        <button>๐Ÿ’พ Submit</button>
    </p>
</div>
const myForm = new SmarkForm(document.getElementById("myForm"));

Ok: Nothing exciting happended by now.

๐Ÿ‘‰ But keep readingโ€ฆ ๐Ÿš€

Do the magic

By default, SmarkForm ignores all DOM elements in its container unless they are marked with the data-smark attribute.

This is because you can have html fields or buttons that belong to other components or functionalities of the page and you donโ€™t want them to be taken as part of the form.

Letโ€™s mark all fields, buttons and labelsโ€ฆ by adding a data-smark attribute :

๐Ÿ”—
๐Ÿ—’๏ธ HTML
โš™๏ธ JS
๐Ÿ‘๏ธ Preview
<div id="myForm">
    <p>
        <label data-smark>Name:</label>
        <input type="text" name="name" data-smark>
    </p>
    <p>
        <label data-smark>Email:</label>
        <input type="email" name="email" data-smark>
    </p>
    <p>
        <button data-smark='{"action":"clear"}'>โŒ Clear</button>
        <button data-smark='{"action":"export"}'>๐Ÿ’พ Submit</button>
    </p>
</div>
const myForm = new SmarkForm(document.getElementById("myForm"));

Now, if you go to the Preview tab and fill some data in, you can then hit de โŒ Clear button and see that, it already works!! ๐ŸŽ‰

Okay, we actually assigned a specific value to the Clear buttonโ€™s data-smark attribute: {"action":"clear"}. But thatโ€™s all we did!! (We will get back to this in the next section).

Also notice that the for attribute of all <label>s had been removed and they still work (if you click on them, corresponging fields get focus anyway).

All elements with a data-smark attribute are SmarkForm components of a certain type.

๐Ÿ‘‰ SmarkForm componentsโ€™ type is often implicit, either by their tag name (like the <label> elements), their type attribute in case of <input>s or by the presence of the action property that tell us they are action triggers.

Actions and Triggers

SmarkForm components with the action property defined in their data-smark object, are called โ€œtriggersโ€ and they serve to invoke so called โ€œactionsโ€ typically when the trigger is clicked or pressed.

For example, the โŒ Clear button has the action property set to "clear", so when it is clicked, it will trigger the clear action.

However actions must be applied to some component. That component is called the context of the action.

The context of actions called by triggers is, by default, their natural context unless other component is specified through the context property.

The natural context of a trigger is the innermost of its SmarkForm component ancestors that implements that action (Which is the whole form in the previous example since the form component type implements the clear action).

๐Ÿ“Œ In fact, the clear action is implemented by all SmarkForm component types.

๐Ÿ‘‰ Thatโ€™s why the โŒ Clear button cleared the whole form in the previous example.

The context of a trigger can be specified either as a relative path from its natural conext or as an absolute path from the form root.

Letโ€™s add more โŒ Clear buttons to clear each specific field:

๐Ÿ”—
๐Ÿ—’๏ธ HTML
โš™๏ธ JS
๐Ÿ‘๏ธ Preview
<div id="myForm">
    <p>
        <label data-smark>Name:</label>
        <input type="text" name="name" data-smark>
        <button data-smark='{"action":"clear", "context":"name"}'>โŒ Clear</button>
    </p>
    <p>
        <label data-smark>Email:</label>
        <input type="email" name="email" data-smark>
        <button data-smark='{"action":"clear", "context":"email"}'>โŒ Clear</button>
    </p>
    <p>
        <button data-smark='{"action":"clear"}'>โŒ Clear</button>
        <button data-smark='{"action":"export"}'>๐Ÿ’พ Submit</button>
    </p>
</div>
const myForm = new SmarkForm(document.getElementById("myForm"));

Since we cannot insert the trigger buttons inside the <input> fields (unless using a singleton) we had to explicitly set the context of those triggers by the relative path from their natural context.

๐Ÿ‘‰ Now you can either clear the whole form by clicking the โŒ Clear button or just clear each field individually by clicking the โŒ Clear button next to each field.

Exporting data

Although it may seem the opposite, the ๐Ÿ’พ Submit button is already working. Itโ€™s just we configured it to trigger the export action but we havenโ€™t told it what to do with exported data.

๐Ÿ‘‰ This can be done by listening to the AfterAction_export event.

The export of the form comes as JSON in the data property of the event object.

Example:

๐Ÿ”—
๐Ÿ—’๏ธ HTML
โš™๏ธ JS
๐Ÿ‘๏ธ Preview
๐Ÿ“ Notes
<div id="myForm">
    <p>
        <label data-smark>Name:</label>
        <input type="text" name="name" data-smark>
    </p>
    <p>
        <label data-smark>Email:</label>
        <input type="email" name="email" data-smark>
    </p>
    <p>
        <button data-smark='{"action":"clear"}'>โŒ Clear</button>
        <button data-smark='{"action":"export"}'>๐Ÿ’พ Submit</button>
    </p>
</div>
const myForm = new SmarkForm(document.getElementById("myForm"));

/* Show exported data in an alert() window */
myForm.on("AfterAction_export", (event)=>{
    window.alert(JSON.stringify(event.data, null, 4));
});

๐Ÿ‘‰ Alternatively, most event handlers can be provided at once through the options object.

๐Ÿ‘‰ Now go to the Preview tab, fill some data in and try clicking the ๐Ÿ’พ Submit button.

Everything works now. ๐ŸŽ‰

All SmarkForm actions are just special methods that can be called from properly placed trigger components avoiding all the burden of manually wiring user interactions.

This enormously simplifies form controllersโ€™ implementation, but they can also be be programatically invoked whenever required.

const {data} = await myForm.export();

Importing data

To load (JSON) data into the form, we use the import action which works in a similar way to the export action but in the opposite direction.

๐Ÿ”—
๐Ÿ—’๏ธ HTML
โš™๏ธ JS
๐Ÿ‘๏ธ Preview
<div id="myForm">
    <p>
        <label data-smark>Name:</label>
        <input type="text" name="name" data-smark>
    </p>
    <p>
        <label data-smark>Email:</label>
        <input type="email" name="email" data-smark>
    </p>
    <p>
        <button data-smark='{"action":"import"}'>๐Ÿ“‚ Import</button>
        <button data-smark='{"action":"clear"}'>โŒ Clear</button>
        <button data-smark='{"action":"export"}'>๐Ÿ’พ Submit</button>
    </p>
</div>
const myForm = new SmarkForm(document.getElementById("myForm"));

/* Show exported data in an alert() window */
myForm.on("AfterAction_export", (event)=>{
    window.alert(JSON.stringify(event.data, null, 4));
});

/* Import data from prompt() window */
myForm.on("BeforeAction_import", async (ev)=>{
    /* Read new value: */
    const json_template = '{"name": "", "email": ""}'; /* Little help to edit */
    let data = window.prompt("Edit JSON data", json_template);
    if (data === null) return void ev.preventDefault(); /* User cancelled */
    /* Parse as JSON, warn if invalid and set */
    try {
        data = JSON.parse(data); ev.data = data; /* โ† Set the new value */
    } catch(err) {
        alert(err.message); /* โ† Show error message */
        ev.preventDefault();
    };
});

Form traversing

As we already stated, actions can be triggered both by interacting with trigger components or by calling them programatically like in the following example we already saw before:

const {data} = await myForm.export();

And the SmarkForm root form is just a field of the type โ€œformโ€.

What if we want to interact directly with a specific part of the form?

Here the .find() method of every SmarkForm field comes to the rescue:

It takes a single argument with a โ€œpath-likeโ€ route to the field we want to access.

This path can be either relative (to the current field) or absolute (to the form root).

๐Ÿ”—
๐Ÿ—’๏ธ HTML
โš™๏ธ JS
๐Ÿ‘๏ธ Preview

<div id='myForm'>
    <p>
        <label for='id'>Id:</label>
        <input data-smark type='text' name='id' />
    </p>
    <fieldset data-smark='{"type":"form","name":"personalData"}'>
    <legend>Personal Data:</legend>
        <p>
            <label for='name'>Name:</label>
            <input data-smark type='text' name='name' placheolder='Family name'/>
        </p>
        <p>
            <label for='surname'>Surname:</label>
            <input data-smark type='text' name='surname' />
        </p>
        <p>
            <label for='address'>Address:</label>
            <input data-smark type='text' name='address' />
        </p>
    </fieldset>
    <fieldset data-smark='{"type":"form","name":"businessData"}'>
    <legend>Business Data:</legend>
        <p>
            <label for='name'>Company Name:</label>
            <input data-smark type='text' name='name' placheolder='Company Name'/>
        </p>
        <p>
            <label for='address'>Address:</label>
            <input data-smark type='text' name='address' />
        </p>
    </fieldset>
</div>

const myForm = new SmarkForm(document.getElementById("myForm"));
/* Set business name */
myForm.onRendered(async ()=>{
    myForm.find("/businessData").import({data: {name: "Bitifet"}});
     /* ๐Ÿ‘‰ Since we don't provide the address field, it will be cleared */
});

Personal Data:

Business Data:

Context and Target

As weโ€™ve seen, when we trigger an action, we (implicitly or explicitly) provide a context to it.

๐Ÿ‘‰ The context of an action is the component over which the action will be applied.

โ†’ When called from a trigger component, the context is determined by the property of the same name in the data-smark object of the trigger if provided, or by the natural context of the trigger otherwise.

โ†’ When called programatically, the context is simply the component over which the action method is called.

๐Ÿ‘‰ In addition to the context many actions also accept another parameter called โ€œtargetโ€.

โ†’ The target is the contextโ€™s child over which the action will be applied.

โ†’ โ€ฆit is determined in a similar way to the context: If the direct parent of the trigger component does not implement the action (and hence cannot be the context), then the greandparent is checked in turn and, if so, the direct parent is picked as the target. And so on until the context is found.

This may seem confusing at first but, as you get used to it, you get the superpower of connecting actions to their context and target just by placing them in the propper place in the DOM tree.

๐Ÿ“Œ And, whenever it is not possible, you only need to specify the relative (or absolute) path in the context and/or target properties of the data-smark object of the trigger component..

Example 1: Lists

For instance, the addItem and removeItem actions are implemented only by the list component type so, if you put a removeItem trigger inside a list item, it will remove that item from the list. No wiring code needed!

๐Ÿ”—
๐Ÿ—’๏ธ HTML
โš™๏ธ JS
๐Ÿ‘๏ธ Preview
๐Ÿ“ Notes
<div id="myForm">
    <p>
        <label data-smark>Phones:</label>
        <ul data-smark='{"name": "phones", "of": "input"}'>
            <li>
                <label data-smark>๐Ÿ“ž </label>
                <input placeholder='+34...' type="tel" data-smark>
                <button data-smark='{"action":"removeItem"}' title='Remove Phone'>โŒ</button>
            </li>
        </ul>
        <button data-smark='{"action":"addItem","context":"phones"}' title='Add Phone'>โž• </button>
    </p>
</div>
const myForm = new SmarkForm(document.getElementById("myForm"));

๐Ÿ‘‰ This example uses the singleton pattern which is out of the scope of this section. But, by now, you can just think of the list items as SmarkForm field components of the type specified in the of property of the list component.

In the case of the addItem action, it will add a new item to the list after the SmarkForm field in which is placed (or before if the position option is set to โ€œbeforeโ€).

I wonโ€™t repeat the previous example now, but you will find several examples of this across this manual.

Example 2: Import and Export targets

Even all SmarkForm field compoenent types implement both the export and import actions, so that the target will always default to null, we can explicitly set the target in import and/or export triggers:

๐Ÿ‘‰ When the import action is called with an explicit target, it will call the export action of the target and import the data to its context.

๐Ÿ‘‰ Similarly, when the export action is called with an explicit target, it will call the import action of the target with the exported data.

๐Ÿš€ This means we can clone list items or import/export whole subforms into other fields (no matter their type -since they will do their best to import received data-) with no single line of JS code like in the following example:

๐Ÿ”—
๐Ÿ—’๏ธ HTML
โš™๏ธ JS
๐Ÿ‘๏ธ Preview
<div id="myForm">
    <div style="display: flex; flex-direction:column; align-items:left; gap: 1em">
        <div data-smark='{"name":"demo"}' style="flex-grow: 1">
            <div id="myForm">
                <p>
                    <label data-smark>Name:</label>
                    <input type="text" name="name" data-smark>
                </p>
                <p>
                    <label data-smark>Email:</label>
                    <input type="email" name="email" data-smark>
                </p>
            </div>
        </div>
        <div style="display: flex; justify-content: space-evenly">
            <span><button
                data-smark='{"action":"export","context":"demo","target":"../editor"}'
                title="Export 'demo' subform to 'editor' textarea"
                >โฌ‡๏ธ Export</button></span>
            <span><button
                data-smark='{"action":"import","context":"demo","target":"../editor"}'
                title="Import 'editor' textarea contents to 'demo' subform"
                >โฌ†๏ธ Import</button></span>
            <span><button
                data-smark='{"action":"clear", "context":"demo"}'
                title="Clear the whole form"
                >โŒ Clear</button></span>
        </div>
        <textarea
            cols="20"
            placeholder="JSON data viewer / editor"
            data-smark='{"name":"editor","type":"input"}'
            style="resize: vertical; align-self: stretch; min-height: 8em; flex-grow: 1;"
        ></textarea>
    </div>
</div>
const myForm = new SmarkForm(document.getElementById("myForm"));

โšก Check the JS tab to see there is no import/export JS code at all and the preview tab to see how it works perfectly.

This trick is used in almost all the examples in the rest of this manual to provide the export/edit/import facilities except for that, unlike here, the surrounding layout is hidden for the sake of clarity.

Go furtherโ€ฆ

However, if it were a larger form, we might not feel so comfortable with the โŒ Clear button (โ€œclearโ€ action trigger) clearing everything in case of an accidental click.

Luckily, we can listen to the BeforeAction_clear event and gently ask users for confirmation before they lose all their work.

Letโ€™s see a simple example using a window.confirm() dialog:

๐Ÿ”—
๐Ÿ—’๏ธ HTML
โš™๏ธ JS
๐Ÿ‘๏ธ Preview
<div id="myForm">
    <p>
        <label data-smark>Name:</label>
        <input type="text" name="name" data-smark>
    </p>
    <p>
        <label data-smark>Email:</label>
        <input type="email" name="email" data-smark>
    </p>
    <p>
        <button data-smark='{"action":"import"}'>๐Ÿ“‚ Import</button>
        <button data-smark='{"action":"clear"}'>โŒ Clear</button>
        <button data-smark='{"action":"export"}'>๐Ÿ’พ Submit</button>
    </p>
</div>
const myForm = new SmarkForm(document.getElementById("myForm"));

/* Show exported data in an alert() window */
myForm.on("AfterAction_export", (event)=>{
    window.alert(JSON.stringify(event.data, null, 4));
});

/* Import data from prompt() window */
myForm.on("BeforeAction_import", async (ev)=>{
    /* Read new value: */
    const json_template = '{"name": "", "email": ""}'; /* Little help to edit */
    let data = window.prompt("Edit JSON data", json_template);
    if (data === null) return void ev.preventDefault(); /* User cancelled */
    /* Parse as JSON, warn if invalid and set */
    try {
        data = JSON.parse(data); ev.data = data; /* โ† Set the new value */
    } catch(err) {
        alert(err.message); /* โ† Show error message */
        ev.preventDefault();
    };
});

/* Ask for confirmation unless form is already empty: */
myForm.on("BeforeAction_clear", async ({context, preventDefault}) => {
    if (
        ! await context.isEmpty()     /* Form is not empty */
        && ! confirm("Are you sure?") /* User clicked the "Cancel" btn. */
    ) {
        /* Prevent default (clear form) behaviour: */
        preventDefault();
    };
});

๐Ÿ‘‰ Notice that now, if you go to the Preview tab and click the โŒ Clear button before introducing any data nothing seems to happen (since the form is already empty).

๐Ÿš€ But, if you fill some data in and then click again, it will effectively ask before clearing the data.

Customize your form

Now you have a SmarkForm-enhanced form. SmarkForm will automatically handle form submission, validation, and other interactions based on the provided markup and configuration.

๐Ÿ‘‰ You can customize the behavior and appearance of your SmarkForm form by configuring options and adding event listeners. SmarkForm provides a wide range of features and capabilities to simplify form development and enhance user experience.

๐Ÿ‘‰ Start exploring the Core Concepts section for a deeper understanding of what is going on and continue with the rest of this SmarkForm manual for more details and examples to discover all the possibilities and unleash the power of markup-driven form development.

๐Ÿ”Ž Here you have a few examples of a larger forms that demonstrates some additional capabilities of the library:

List of telephones:

๐Ÿ”—
๐Ÿ—’๏ธ HTML
๐ŸŽจ CSS
โš™๏ธ JS
๐Ÿ‘๏ธ Preview
๐Ÿ“ Notes
<div id="myForm">
<div data-smark='{"name":"phones","type":"list","of":"input","max_items":6,"sortable":true}'>
    <div>
    <button data-smark='{"action":"removeItem","hotkey":"-"}' title='Remove this item'><span role='img' aria-label='Remove this item'>โž–</span></button>
    <input data-smark='data-smark' type='tel' placeholder='Telephone'/>
    <button data-smark='{"action":"addItem","hotkey":"+"}' title='Add new item below'><span role='img' aria-label='Add new item'>โž•</span></button>
    </div>
</div>
<p>
    <button data-smark='{"action":"clear"}'>โŒ Clear</button>
    <button data-smark='{"action":"export"}'>๐Ÿ’พ Submit</button>
</p>
</div>
/* Emphasize disabled buttons */
button:disabled {
    opacity: .5;
}

/* Reveal hotkey hints */
button[data-hotkey] {
    position: relative;
    overflow-x: display;
}
button[data-hotkey]::before {
    content: attr(data-hotkey);

    display: inline-block;
    position: absolute;
    top: 2px;
    left: 2px;
    z-index: 10;

    background-color: #ffd;
    outline: 1px solid lightyellow;
    padding: 1px 4px;
    border-radius: 4px;
    font-weight: bold;

    transform: scale(1.4) translate(.1em, .1em);

}
const myForm = new SmarkForm(document.getElementById("myForm"));

/* Show exported data in an alert() window */
myForm.on("AfterAction_export", (event)=>{
    window.alert(JSON.stringify(event.data, null, 4));
});

/* Import data from prompt() window */
myForm.on("BeforeAction_import", async (ev)=>{
    /* Read new value: */
    const json_template = '{"name": "", "email": ""}'; /* Little help to edit */
    let data = window.prompt("Edit JSON data", json_template);
    if (data === null) return void ev.preventDefault(); /* User cancelled */
    /* Parse as JSON, warn if invalid and set */
    try {
        data = JSON.parse(data); ev.data = data; /* โ† Set the new value */
    } catch(err) {
        alert(err.message); /* โ† Show error message */
        ev.preventDefault();
    };
});

/* Ask for confirmation unless form is already empty: */
myForm.on("BeforeAction_clear", async ({context, preventDefault}) => {
    if (
        ! await context.isEmpty()     /* Form is not empty */
        && ! confirm("Are you sure?") /* User clicked the "Cancel" btn. */
    ) {
        /* Prevent default (clear form) behaviour: */
        preventDefault();
    };
});

๐Ÿ‘‰ Limited to a maximum of 6 numbers.

๐Ÿ‘‰ Notice trigger butons get properly disabled when limits reached.

๐Ÿ‘‰ They are also excluded from keyboard navigation for better navigation with tab / shift+tab.

๐Ÿ‘‰ Even thought they can be triggered from keyboard through configured (Ctrl-+ and Ctrl--`) hot keys.

๐Ÿ‘‰ โ€ฆNotice what happen when you hit the ctrl key while editingโ€ฆ

๐Ÿ‘‰ Also note that, when you click the ๐Ÿ’พ Submit button only non empty items get exported.

๐Ÿ‘‰ โ€ฆYou can prevent this behaviour by setting the exportEmpties property to true.

๐Ÿ‘‰ Change items order by just dragging and dropping them.

Simple schedule:

๐Ÿ”—
๐Ÿ—’๏ธ HTML
โš™๏ธ JS
๐Ÿ‘๏ธ Preview
<div id="myForm">
<p>
    <button data-smark='{"action":"removeItem","hotkey":"-","context":"surveillance_schedule"}' title='Less intervals'>
        <span role='img' aria-label='Remove interval'>โž–</span>
    </button>
    <button data-smark='{"action":"addItem","hotkey":"+","context":"surveillance_schedule"}' title='More intrevals'>
        <span role='img' aria-label='Add new interval'>โž•</span>
    </button>
    <label>Schedule:</label>
    <span data-smark='{"type":"list","name":"surveillance_schedule","min_items":0,"max_items":3,"exportEmpties":true}'>
        <span>
            <input data-smark type='time' name='start'/>
            to
            <input data-smark type='time' name='end'/>
        </span>
        <span data-smark='{"role":"empty_list"}'>(Closed)</span>
    </span>
</p>
<p>
    <button data-smark='{"action":"clear"}'>โŒ Clear</button>
    <button data-smark='{"action":"export"}'>๐Ÿ’พ Submit</button>
</p>
</div>
const myForm = new SmarkForm(document.getElementById("myForm"));

/* Show exported data in an alert() window */
myForm.on("AfterAction_export", (event)=>{
    window.alert(JSON.stringify(event.data, null, 4));
});

/* Import data from prompt() window */
myForm.on("BeforeAction_import", async (ev)=>{
    /* Read new value: */
    const json_template = '{"name": "", "email": ""}'; /* Little help to edit */
    let data = window.prompt("Edit JSON data", json_template);
    if (data === null) return void ev.preventDefault(); /* User cancelled */
    /* Parse as JSON, warn if invalid and set */
    try {
        data = JSON.parse(data); ev.data = data; /* โ† Set the new value */
    } catch(err) {
        alert(err.message); /* โ† Show error message */
        ev.preventDefault();
    };
});

/* Ask for confirmation unless form is already empty: */
myForm.on("BeforeAction_clear", async ({context, preventDefault}) => {
    if (
        ! await context.isEmpty()     /* Form is not empty */
        && ! confirm("Are you sure?") /* User clicked the "Cancel" btn. */
    ) {
        /* Prevent default (clear form) behaviour: */
        preventDefault();
    };
});

to (Closed)

Now that you understand the basics maybe a good opportunity to revisit the Showcase section and examine the source code of those examples.

๐Ÿš€ Also donโ€™t miss the Examples section for more complete and realistic use casesโ€ฆ

Final notes

Boilerplate file

If you prefer to start from scratch, congratulations!!

But, be aware that SmarkForm is intended to not interfere with your HTML and CSS.

It adds great functionality but does nothing with the layout and styling of the page or even its components.

Nevertheless, if you liked the styling of our examples and want to start a new project, donโ€™t miss our Boilerplate Template.

You can pick it from the Resources โžก๏ธ Download section of this manual.

You donโ€™t need a form tag

You donโ€™t even need a <form> tag. In fact it is not (yet) advised to use it for SmarkForm forms.

If you do so, they will be submit-prevented so they can act as kind of failback behvaviours in case of JavaScript being disabled.

But itโ€™s not yet clear which could be a future enhancenment of native <form> attributes, such as action, in successfully enhanced <form> tags.

Using your own copy of SmarkForm library

If you donโ€™t want to rely on an external resource like a CDN, take the following steps:

<script src="path/to/SmarkForm.js"></script>

Alternative forms to get/include Smarkform

There is plenty of ways to get SmarkForm. Either as ESM module or highly compatible UMD format you can download it from this page, install as npm package, directly link as a CDNโ€ฆ

For more detailed information, check out the section Getting SmarkForm.

The optionโ€™s object

If you want a more compact syntax to provide, at least, most basic functionality at once, you can use the options object.

const myForm = new SmarkForm(
    document.getElementById("myForm")
    , {
        onAfterAction_export({data}) {
            // Show exported data:
            console.log(data);
        },
    }
);

All AfterAction_xxx and BeforeAction_xxx events can get their first event handler attached through functions respectively named onAfterAction_xxx and onBeforeAction_xxx in the options object passed to the SmarkForm constructor.

The only differrence here is that AfterAction_xxx and BeforeAction_xxx events can be locally attached to any SmarkForm field.

Handlers passed through that options are attached to the root form.



Hello footer