In this guide you'll see how to attach component related scripts and deal with external JavaScript libraries (for stuff like counters, galleries, slideshows, etc.)
In this guide you'll see how to attach component related scripts and deal with external JavaScript libraries (eg. counters, galleries, slideshows, etc.)
::: warning
This guide is referring to GrapesJS v0.16.31 or higher.<br><br>
To get a better understanding of the content in this guide, we recommend reading [Components](Components.html) and [Traits] first
:::
[[toc]]
## Basic scripts
Let's see how to create a component with scripts using Blocks.
// Add some style just to make the component visible
style: {
width: '100px',
height: '100px',
'background-color': 'red',
// This is our custom script (avoid using arrow functions)
const script = function() {
alert('Hi');
// `this` is bound to the component element
console.log('the element', this);
};
// Define a new custom component
editor.Components.addType('comp-with-js', {
model: {
defaults: {
script,
// Add some style, just to make the component visible
style: {
width: '100px',
height: '100px',
background: 'red',
}
}
}
});
// Create a block for the component, so we can drop it easily
editor.Blocks.add('test-block', {
label: 'Test block',
attributes: { class: 'fa fa-text' },
content: { type: 'comp-with-js' },
});
```
Now if you drag the new block inside the canvas you'll see an alert and the message in console, as you might expect.
One thing worth noting is that `this` context is bound to the component element, so if you wanted to change a property you'd do `this.innerHTML = 'inner content'`.
One thing worth noting is that `this` context is bound to the component's element, so, for example, if you need to change its property, you'd do `this.innerHTML = 'inner content'`.
One thing you should take in account is how the script is bound to component once rendered in the canvas or in your final template. If you check now the generated HTML coded by the editor (via Export button or `editor.getHtml()`), you might see something like this:
One thing you should take into account is how the script is bound to component once rendered in the canvas or in your final template. If now you check the generated HTML code in the editor (via Export button or `editor.getHtml()`), you might see something like this:
```html
<divid="c764"></div>
@ -66,38 +86,26 @@ As you see the editor attaches a unique ID to all components with scripts and re
</script>
```
Keep in mind that all component scripts are executed only inside the iframe of the canvas (isolated, just like your final template), and therefore are NOT part of the current `document`. All your external libraries (eg. jQuery) are not there, but you'll see later how to manage scripted components with dependencies.
## Important caveat
One thing you might be concerned about is a string used for the `script`. Definitely not the best way to deal with a code, for this reason GrapesJS is also able to handle functions for you, so the previous example might look like this:
::: danger
Read carefully
:::
```js
editor.BlockManager.add('test-block', {
...
content: {
script: function () {
alert('Hi');
console.log('the element', this);
},
...
}
});
```
This is much better, but be aware though, that the function body is converted to string internally, before it can be placed in the document, so you can't use variables outside of the function scope. Take a look at this scenario:
Keep in mind that all component scripts are executed inside the iframe of the canvas (isolated, just like your **final template**), and therefore are NOT part of the current `document`. All your external libraries (eg. those load along with the editor) are not there (you'll see later how to manage scripted components with dependencies).
That means **you can't use stuff outside of the function scope**. Take a look at this scenario:
```js
var myVar = 'John';
const myVar = 'John';
editor.BlockManager.add('test-block', {
...
script: function () {
alert('Hi ' + myVar);
console.log('the element', this);
},
...
});
const script = function() {
alert('Hi ' + myVar);
console.log('the element', this);
};
```
Unfortunately, this won't work. You'll get an undefined `myVar` error. The final HTML, with script functions converted to string, will look like this:
This won't work. You'll get an error of the undefined `myVar`. The final HTML shows the reason more clearly
```html
<divid="c764"></div>
@ -105,59 +113,60 @@ Unfortunately, this won't work. You'll get an undefined `myVar` error. The final
var items = document.querySelectorAll('#c764');
for (var i = 0, len = items.length; i <len;i++){
(function(){
// START component code
alert('Hi ' + myVar); // <-ERROR:undefinedmyVar
console.log('the element', this);
// END component code
}.bind(items[i]))();
}
</script>
```
There is a solution to make your scripts behave dynamically. You can interpolate properties of the component model.
```js
editor.BlockManager.add('test-block', {
...
content: {
myModelPropName: 'John',
script: function () {
alert('Hi {[ myModelPropName ]}');
console.log('the element', this);
},
...
}
});
```
The final HTML will be:
## Passing properties to scripts
```html
<divid="c764"></div>
<script>
var items = document.querySelectorAll('#c764');
for (var i = 0, len = items.length; i <len;i++){
(function(){
alert('Hi John');
console.log('the element', this);
}.bind(items[i]))();
}
</script>
```
You can even change the tags used for the interpolation
Let's say you need to make the script behave differently, based on some component property, maybe also changable via [Traits] (eg. you want to initiliaze some library with different options).
You can do it by using the `script-props` property on your component.
```js
var editor = grapesjs.init({
...
// Default values
tagVarStart: '{[ ',
tagVarEnd: ' ]}',
...
// The `props` argument will contain only the properties you have declared in `script-props`