Back to How-Tos

Speed Up your Connect template

​​​Page rendering in Connect Designer is handled by an embedded web browser. For styling and personalization it makes use of CSS and JavaScript. Therefore common web page optimizations techniques apply to Connect templates too. In this article we cover some basic tricks to speed up your Connect template.

  1. Use a selector for Text scripts
  2. Cleanup your scripts
  3. Avoid DOM manipulation when possible
  4. Use string replace() in scripts to personalize snippets
  5. Extract, treat and put back
  6. Bonus: Optimize dynamic images

Note: Results may vary depending on the type of document you are creating, structure of the layout and the level of personalization.

Use a selector for Text scripts

The easiest way to add personalized information to your document is by dragging and dropping a data field from the Data Model panel to the main editor. This inserts a placeholder string at the cursor position with the name of the data field (e.g. @firstname@). A Text script to replace that string with the data value is added to the Scripts panel. These Text scripts perform a find/replace action by searching the entire content of the document. When there is a lot of content this action may slow down the output performance of the document.

This can be optimized by narrowing the search scope by pointing the script to the element containing the placeholder. This is achieved by assigning an ID to the element containing the placeholder and by changing the search scope of the Text script. This results in a very fast query as elements with an ID are indexed by the layout engine.

To set an ID:

  • Select the container, typically this is a box (e.g. a <div> element) or a table cell.
  • Set the ID via the Attributes panel.

Once setup you need to change the search scope of the Text script. Double click the script entry and change the find method to Selector + Text or just the Selector when the placeholder is the only content of the container. Enter the ID of element in the Selector field. For example: #first-name-box.

Tip! Create a container that solely wraps your placeholder string. Select the placeholder string and choose: Insert > Text > Wrap in Span… or select Wrap in Span… from the contextual menu. This option wraps the string in a element and dialog that appears lets you set an ID on the fly. In this scenario set the find method of the script to Selector and enter the ID in the Selector field (e.g. #firstname).

Clean up your scripts

When creating a template you may not use all scripts located in the Scripts panel. You can verify this by running a Preflight (choose: Context > Preflight). Scripts that didn't return a result still take up time as the embedded browser has to execute the query. Especially unused Text scripts that rely solely on the Text find method can slow down your template.

Avoid DOM manipulation when possible

The scripting API of the Designer is a very powerful tool to manipulate and personalize your document. But keep in mind that DOM manipulation commands like append(), prepend(), before() and after() are resource intensive.

So for try avoiding DOM modifications within loops. In those scenarios it's more efficient to store content into a variable and append the information after the loop. In that scenario your template is touched only once.

Consider the following example. It loads a snippet into a variable and inserts the personalized information within the loop using a find() and textI() commands of the Designer scripting API.

labelElm = loadhtml('snippets/label.html');

for( var i = 0; i < 100; i++) {
       var label = labelElm.clone();
       label.find('@idx@').text(i);
       results.after(label);
}

In the above scenario the after() command is run 100 times. The script below shows how the personalized content is added to a string called labelStr. This string is added to the DOM after our for loop and therefore the DOM is touched only once.

var labelElm = loadhtml('snippets/label.html');
var labelStr = "";

for( var i = 0; i < 100; i++) {
      var label = labelElm.clone();
      label.find('@idx@').text(i);
      labelStr += label;
} 

results.after(labelStr);

Use string replace() in scripts to personalize snippets

When personalizing HTML fragments retrieved from a snippet or fragments retrieved from the document itself the best performance is shown when using the expression driven replace() method from JavaScript.

The following script retrieves a snippet using the loadhtml() command. By default this command returns a QueryResul. This allows you to perform DOM manipulation tasks like adding/removing elements, add/remove CSS classes etc. When the tasks are limited to find/replace actions you could use the toString() method to convert the QueryResult into a string. This allows you to replace text using the replace() method.

var labelSnippet = loadhtml('snippets/label.html').toString();
var labelStr = "";

for( var i = 0; i <= 100; i++) {
     var label = labelSnippet; 
     label = label.replace('#', i);
     label = label.replace('@product@', record.tables.detail[i].fields['product']);
     label = label.replace('@notes@', record.tables.detail[i].fields['notes']);
     label = label.replace('@netweight@', record.tables.detail[i].fields['netweight']);
     labelStr += label;
}

results.after(labelStr);

Tip! The replace() method normally replaces the first occurrence of the search string. This method allows you to pass a regular expression, which can be used to perform a global search. The following method performs a global search on the string @product@:

label = label.replace(/@product@/g, record.tables.detail[i].fields['product']);

Extract, treat and put back

When performing multiple tasks within an element (e.g. replace a range of placeholder) you might want to consider the following method: create a script to extract the element, personalize it and put the result back where it came from. This is similar to working with snippets but in this case the source elements are extracted from the actual layout. It will reduce the number of scripts executed and for that improve the performance of the template.

Let's say you are replacing 20 different placeholders in a postcard (address info, account/customer details, a promo code, due date, discounts, a link to personalized landing page etc etc). Typically this would result in 20 queries and after reading this article you optimized these scripts by setting an ID selector for those elements. But it still involves 20 queries.

When using the extract, treat and put back approach you will only have a single query and we learned that the JavaScript replace() command works very fast. So the script for our postcard will look like the code below where we assume that the block that requires the personalization has its ID set to promoblock:

Selector: #promoblock

 var block = results.html();
 var data = record.fields;

 block = block.replace('@name@',data.first + ' ' + data.last);
 block = block.replace('@address@',data.address);
 block = block.replace('@zip@',data.zip);
 block = block.replace('@city@',data.city);
 block = block.replace('@country@',data.country);
 block = block.replace('@saldo@',data.saldo);
 block = block.replace('@promo@',data.promo);
 block = block.replace('@customercode@', data.customercode);
 …

 results.html(block);

On the first line we retrieve the HTML of the promo block and store that in a variable called block. To keep the code clean we store the fields from the record in a variable with the name data. This will make the subsequent script lines easier to read.

Now we perform the various replace actions. Once we completed the HTML of the promoblock is replaced with our personalized string.

Bonus tip

Optimize dynamic images Perhaps not trivial and very obvious but worth mentioning:

Retrieving images dynamically from a large collection of files generates multiple file requests, these can take up a lot of time. The solution is simple… combine the images into a single image file and display the part that holds our image. This reduces the number of file requests and can show a significant speed improvement. In web design this technique is called a Sprite image or CSS Sprite image.

For our Print documents we can achieve the same by storing the images in a single .pdf file (one image per page) and subsequently retrieve the page that contains the required image. As stated it reduces the number of file requests but it requires you to remember the page number of the each image.

The following script iterates over 100 images using a for loop. In each iteration an image is fetched and added to a string. Once the loop is completed the string is added to the document using the after() command. In this scenario the image names are a simple sequence: 1.jpg, 2.jpg, 3.jpg etc etc:

var imageStr = "";

for( var i = 1; i <= 100; i++ ){
      var imagePath = "file:///C:/images/" + i + '.jpg';
      imageStr += '<img src="' + imagePath + '">';
}

results.after(imageStr);

The above script performs 100 file requests. Now let us combine these images into a single .pdf file and retrieve the respective page (which in this case is very simple as our image files are numbered). By adding the page parameter to the file path the respective page (in this case our image) is retrieved from the .pdf.

var imageStr = "";

for( var i = 1; i <= 100; i++ ){
      var imagePath = "file:///C:/image-collection.pdf?page=" + i;
      imageStr += '<img src="' + imagePath + '">';
}

results.after(imageStr);

Tools to combine image files into a single pdf are:

ImageMagick Use the convert command of the ImageMagick library:

convert C:/myimages/*.jpg C:/myimages/image-collection.pdf

Connect Designer Correct, you can use Connect Designer for that too. Create a print template with the size of your images and set the page margins to 0. Create a script that loops over your images and adds them to the text flow of the document. Subsequently generate PDF output. Use the resulting file as your collection file.