Components & JS

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.)

Basic scripts

Let's see how to create a component with scripts using Blocks.

editor.BlockManager.add('test-block', {
  label: 'Test block',
  attributes: {class: 'fa fa-text'},
  content: {
    script: "alert('Hi'); console.log('the element', this)",
    // Add some style just to make the component visible
    style: {
      width: '100px',
      height: '100px',
      'background-color': 'red',
    }
  }
});

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 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:

<div id="c764"></div>
<script>
  var items = document.querySelectorAll('#c764');
  for (var i = 0, len = items.length; i < len; i++) {
    (function(){
      // START component code
      alert('Hi');
      console.log('the element', this)
      // END component code
    }.bind(items[i]))();
  }
</script>

As you see the editor attaches a unique ID to all components with scripts and retrieves them via querySelectorAll. Dragging another test-block will generate this:

<div id="c764"></div>
<div id="c765"></div>
<script>
  var items = document.querySelectorAll('#c764, #c765');
  for (var i = 0, len = items.length; i < len; i++) {
    (function(){
      // START component code
      alert('Hi');
      console.log('the element', this)
      // END component code
    }.bind(items[i]))();
  }
</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.

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 able also to handle functions for you, so the previous example might look like this:

editor.BlockManager.add('test-block', {
  ...
  content: {
    script: function () {
      alert('Hi');
      console.log('the element', this);
    },
    ...
  }
});

This is much better, but be aware of a string conversion, you can't use variables outside of the function scope. Take a look at this scenario:

var myVar = 'John';

editor.BlockManager.add('test-block', {
...
  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:

<div id="c764"></div>
<script>
  var items = document.querySelectorAll('#c764');
  for (var i = 0, len = items.length; i < len; i++) {
    (function(){
      // START component code
      alert('Hi ' + myVar); // <- ERROR: undefined myVar
      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.

editor.BlockManager.add('test-block', {
  ...
  content: {
    myModelPropName: 'John',
    script: function () {
      alert('Hi {[ myModelPropName ]}');
      console.log('the element', this);
    },
    ...
  }
});

The final HTML will be:

<div id="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

var editor = grapesjs.init({
  ...
  // Default values
  tagVarStart: '{[ ',
  tagVarEnd: ' ]}',
  ...
});

You can use this technique with property Traits to create custom components.

Dependencies

As we mentioned above, scripts are executed independently inside the iframe of the canvas, without any dependencies, so exactly as the final HTML generated by the editor. If you want to make use of external libraries you have two approaches: component-related and template-related.

If you're building a slider component based on some third-party library you probably would like to include the external file only when the component is actually dragged inside the canvas. In this case, the component-related approach is the perfect one as it's loading external libraries dynamically. All you have to do is to require the dependency when it is needed and then call your script.

...
script: function () {
  var el = this;
  var initMySLider = function() {
    CoolSliderJS.init(el);
  }

  if (typeof CoolSliderJS == 'undefined') {
    var script = document.createElement('script');
    script.onload = initMySLider;
    script.src = 'https://.../coolslider.min.js';
    document.body.appendChild(script);
  }
},
...

A dependency might be used along all your components (eg. JQuery) so instead requiring it inside each script you might want to inject it directly inside the canvas:

var editor = grapesjs.init({
  ...
  canvas: {
    scripts: ['https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js']
  }
});

...
  script: function () {
    // Do stuff using jquery
    $('...');
  },
...

Examples

Examples of components using scripts inside

Last Updated: 10/29/2018, 4:01:56 AM