> For the complete documentation index, see [llms.txt](https://docs.wem.io/platform/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.wem.io/platform/tutorials/building-widgets/wemscript/7-script-blocks-introduction.md).

# 7. Script Block - Introduction

In this chapter, we will create a Note widget. It is a relatively simple widget to build, but we need to address a few challenges to ensure it functions correctly as a WEM widget.

As mentioned previously, adding widgets will introduce CSS and JavaScript into a global space. This can be quite challenging for more advanced widgets, as we want to avoid overriding and breaking existing CSS and JavaScript. Therefore, we will start with some scaffolding to see how we can prevent these issues.

We will learn about invariant culture, naming collisions, cleaning up the widget, and a few other concepts along the way. Let's get started!

* Create a new widget and name it "Note."
* Copy and paste the following CSS into a new file called "note.css."
* Add it as a resource named "NoteCss."

```css
.wem-note {
	background-color: lightgoldenrodyellow;
	display: flex;
	flex-direction: column;
	position: fixed;
	left: calc(var(--x) * 1px);
	top: calc(var(--y) * 1px);
	width: 250px;
	height: 250px;
}

.wem-note > .handle-bar {
	background-color: tomato;
	cursor: pointer;
	flex: 0 0 20px;
}

.wem-note > .message {
	flex: 1 0;
	outline: none;
	padding: 8px;
	resize: none;
}
```

Notice that we use a more specific class name for our Note widget, `.wem-note`.

* Copy and paste the JavaScript code below into a new file called "note.js."
* Add it as a resource named "NoteJs."

```javascript
class WemNote {
    messageEl;
    rootEl;
    x; 
    y; 
    previousX; 
    previousY;
    
    constructor({ x = 100, y = 100, message = "New note" }) {
        this.setThisContext();

        const handleBarEl = document.createElement("div");
        handleBarEl.classList.add("handle-bar");
        handleBarEl.addEventListener("mousedown", this.startDragHandler);

        this.messageEl = document.createElement("textarea");
        this.messageEl.classList.add("message");
        this.messageEl.value = message;

        this.rootEl = document.createElement("div");
        this.rootEl.classList.add("wem-note");
        this.rootEl.append(handleBarEl, this.messageEl);
        
        this.updatePosition(x, y);
    }

    dispose() {
        this.removeWindowEventListeners();
    }

    getData() {
        return {
            x: this.x,
            y: this.y,
            message: this.messageEl.value,
        };
    }

    moveHandler(event) {
        const deltaX = event.screenX - this.previousX;
        const deltaY = event.screenY - this.previousY;

        this.previousX = event.screenX;
        this.previousY = event.screenY;

        this.updatePosition(this.x + deltaX, this.y + deltaY);

        // Disable browser selection.
        event.preventDefault();
        event.stopImmediatePropagation();
    }

    removeWindowEventListeners() {
        window.removeEventListener("mousemove", this.moveHandler, { capture: true });
        window.removeEventListener("mouseup", this.removeWindowEventListeners);
    }

    setThisContext() {
        this.moveHandler = this.moveHandler.bind(this);
        this.removeWindowEventListeners = this.removeWindowEventListeners.bind(this);
        this.startDragHandler = this.startDragHandler.bind(this);
    }

    startDragHandler(event) {
        this.previousX = event.screenX;
        this.previousY = event.screenY;
        
        window.addEventListener("mousemove", this.moveHandler, { capture: true });
        window.addEventListener("mouseup", this.removeWindowEventListeners);
    }

    updatePosition(x, y) {
        this.x = x;
        this.y = y;
        this.rootEl.style.setProperty("--x", this.x);
        this.rootEl.style.setProperty("--y", this.y);
    }
}
```

The above CSS and JavaScript code are standalone and can even be used in a non-WEM application if desired. The code below is necessary to make it work as a WEM widget. There are a few interesting aspects that we will dissect later.

```html
<? 
	scriptreference "wem-note" FileUrl(@NoteJs)

	register input @Message 
	register input @X invariant culture
	register input @Y invariant culture
?>
<input type="hidden" id="<?attr OutputId(@Message) ?>" name="<?attr OutputId(@Message) ?>">
<input type="hidden" id="<?attr OutputId(@X) ?>" name="<?attr OutputId(@X) ?>">
<input type="hidden" id="<?attr OutputId(@Y) ?>" name="<?attr OutputId(@Y) ?>">
<? startupscript ?>

	const note = new WemNote({
		message: <?js @Message ?>,
		x: <?js @X ?>,
		y: <?js @Y ?>,
	});

	document.documentElement.append(note.rootEl);

	window[Symbol.for(<?js OutputId() ?>)] = note;

<? end ?>
<? submitscript ?>
	
	const note = window[Symbol.for(<?js OutputId() ?>)];
	const { message, x, y } = note.getData();
	
	document.getElementById(<?js OutputId(@Message) ?>).value = message;
	document.getElementById(<?js OutputId(@X) ?>).value = x;
	document.getElementById(<?js OutputId(@Y) ?>).value = y;
	
<? end ?>
<? unloadscript ?>

	const noteSymbol = Symbol.for(<?js OutputId() ?>);
	window[noteSymbol].dispose();
	delete window[noteSymbol];

<? end ?>
```

### Invariant Culture

An extra argument, `invariant culture`, has been added to the `register input` statements in the following lines of code:

```
register input @X invariant culture
register input @Y invariant culture
```

In the previous encoding section, I explained the different writing formats used by various cultures (for example, in English, a "." is used as a decimal separator, while in Dutch, a "," is used). This discrepancy can make it challenging for programmers to read and write numbers consistently between systems. An "invariant culture" is essentially an artificial culture where the format specifications remain constant. In the case of numbers, the "invariant culture" specifies that the decimal separator will be a ".".

### ID Attribute

In addition to the `name` attribute we encountered in our Simple Form widget, we have now added an `id` attribute to the `<input>` elements in the following lines of code:

```html
<input type="hidden" id="<?attr OutputId(@Message) ?>" name="<?attr OutputId(@Message) ?>">
<input type="hidden" id="<?attr OutputId(@X) ?>" name="<?attr OutputId(@X) ?>">
<input type="hidden" id="<?attr OutputId(@Y) ?>" name="<?attr OutputId(@Y) ?>">
```

The reason we need the `id` attribute here is that we have an additional step in the `submitscript` to retrieve the new values from the note instance, and we want to write those values back to these inputs. The `name` attributes are still mandatory on the `<input>` fields for sending the data back with the request to the WEM runtime.

### Special Script Blocks

You may have noticed that we haven't written any JavaScript inside a `<script>` tag yet. This is not incorrect by any means, and there are situations where it is even necessary when using third-party libraries. However, when building a widget, you want to have more control over when to call functionality in response to specific events. Each piece of JavaScript code in these special JavaScript blocks can be thought of as handlers, similar to a click handler on a button. We have three special JavaScript blocks; let's walk through them.

#### Startup Script

```javascript
<? startupscript ?>

	const note = new WemNote({
		message: <?js @Message ?>,
		x: <?js @X ?>,
		y: <?js @Y ?>,
	});

	document.documentElement.append(note.rootEl);

	window[Symbol.for(<?js OutputId() ?>)] = note;

<? end ?>
```

This code will run after the page has been fully replaced, making it ideal for initializing JavaScript instances since you can be sure that all elements have been rendered. As shown in the example above, we initialize our note here. You may be wondering about the `Symbol.for` statement; I will explain that later.

#### Submit Script

```javascript
<? submitscript ?>

    const note = window[Symbol.for(<?js OutputId() ?>)];
    const { message, x, y } = note.getData();

    document.getElementById(<?js OutputId(@Message) ?>).value = message;
    document.getElementById(<?js OutputId(@X) ?>).value = x;
    document.getElementById(<?js OutputId(@Y) ?>).value = y;

<? end ?>
```

This code executes after an event has been triggered, such as a button click, but before the request is made. It is ideal for sanitizing and serializing data, as well as updating the data just before sending it with the request. This is why we need the `id` attribute on the input fields.

#### Unload Script

```javascript
<? unloadscript ?>

    const noteSymbol = Symbol.for(<?js OutputId() ?>);
    const note = window[noteSymbol];
    note.dispose();

    delete window[noteSymbol];

<? end ?>
```

The code above runs after receiving a new response from the server but before the new page is rendered. It is perfect for cleaning up a widget instance. In this case, we call the `note.dispose()` method to clean up our Note widget. It is always important to perform cleanup, especially when there may still be a callback from `setTimeout()` that needs to be cleared with `clearTimeout()`. Additionally, our Note widget could still have `"mouseup"` and `"mousemove"` event listeners attached to the `window` object. Although optional, deleting the note ensures that there are no lingering references in the JavaScript runtime, which can assist the garbage collector.

### Conclusion

We have built the foundational components to initialize our widget via the `startupscript`, update our form data using the `submitscript`, and clean up our widget in the `unloadscript`. To reduce the risk of naming collisions, we prefixed our class names to make them less generic. In the next chapters, we will explore additional details, such as the purpose of `window[Symbol.for(<?js OutputId() ?>)]` in the code above.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.wem.io/platform/tutorials/building-widgets/wemscript/7-script-blocks-introduction.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
