Chrome Extension Tutorial – Snippets

This is the first post in the series – Building an automatic text expander. The second post in the series is about Placeholders and the third is about Date-Time Macros, Mathomania, and Auto-Inserts

I bet there have been countless times when you have had to write something – that same thing – over and over again. I too had been bugged by this problem for so long; I decided that only a software, which would save my chunks of text and retrieve them at my command, can pull me out of this trouble. I searched a lot of softwares online, but none could really satisfy me. And thus, ProKeys was born!

In this blog post series, I am going to guide you through the process of making such a Chrome extension. You are sure to learn many new things!

Prerequisites

  1. Intermediate knowledge of JavaScript.
  2. Basic knowledge of regular expressions (Spend five minutes reading this brief guide, if you need to, and then come back).
  3. Familiarity with using and setting up Chrome extensions (Must have gone through the docs once, and followed the Getting Started tutorial).

Basic Functioning

Needless to say, we need to know what we have to implement before actually implementing it! We will cover Snippet functioning in this post. A brief overview of Snippet functioning follows: Let’s say I have to type this text again and again:

Hello name,
Thank you for your response. ….. … . . . . . . . .
. . . . . . . . . . . bug number id .. . . . . .
. .. . . . . . . .
Date is date

I will save it as a snippet, assigning it a snip name of, say “customer_post”, and the above text as a snip body. So, snippets are simply pairs of snip names (abbreviations) and snip bodies (the larger texts). You just type the snip name and press the hotkey (like, Shift+Space) to expand the abbreviation to the larger text. As you must have seen, some words in the above text were underlined, because in a real life situation, some portions of text always depend on the context, in which they are being written, and change from context to context. To let users change these words of snip body, at the time of writing, I created Placeholders. They are denoted by “%whatever_word%” in the snip body, and are auto-highlighted when we insert a snip body. So, once they are highlighted users can change it, and then use [Tab] key to switch to the next placeholder.

There are many more functions that ProKeys can perform. I will be covering their implementation in subsequent blog posts. You can watch all of them in one video:

Let’s write some code!

Content scripts are JavaScript files that run in the context of the web page. They are perfect for our need and we will be using them. First of all, add a content script in your manifest.json:

"matches": [""] makes sure the content script runs on all webpages.  "run_at": "document_end" is used while implementing ProKeys for Gmail Rich-Text Editor (the Compose window).  "all_frames”: true makes it run in all the iframes of the webpage. The name I use for the js file is  popup.js .

We will use an immediately-invoked function expression (IIFE), and within it, a window.onload event listener. IIFE is used so that the variable names we use do not clash with variable names of existing web page (even if they can’t; still, take no risks)

The .call method will make handleKeyDown function run in the context of node with one argument event.

Learning point: You might wonder why I haven’t used  document.querySelectorAll(textarea)  to get all the  <textarea>  elements in the page, and then, attached to them, individually, a  keyDown  event? The reason is that, this would only be executed once, on window load. So, document.querySelectorAll(textarea)  will get me those <textarea> s which were present at the time the script ran. But, what if some <textarea>  got dynamically appended to the webpage later on? Then, it would not have the keydown event attached to it, and hence, our extension would not work. The solution is that the document   will be present throughout. So, we can attach a keyDown event to it, and then delegate it to the actual node using event.target . Congrats! You just learned event delegation!

See event delegation live:

Let’s move on to our handleKeyDown function:

Learning Point: event.preventDefault() will prevent the default action associated with the event from happening. In the above case, no space character will get inserted (which is the default action on Shift+Space).

See event.preventDefault() live:

The function  formatSnippets  will do the magic of snippet expansion!

Snippets: The magic starts!

I’ll assume that the snippets are stored in an array, each as an object in the following format:

So, we will have to simply loop through the Snippets array, and check if the snippet name corresponds with the text immediately behind the user’s cursor. If it does, it’s a snippet replacement work to start.

But, after the snippet replacement is done, our caret is left where it was, let’s place it after snippet body:

That’s it! The work’s done. You mainly need to understand the logic of this work; when you know the logic, coding becomes very easy. So, let’s revise the logic:

  1. We loop through the Snippets array. Refer to each current snippet as snipand the length of snip.nameas length
  2. We get X number of characters present immediately before the caret, where X = length.
  3. We compare those characters to snip.name . If they match, we have to replace snip.name  with snip.body .
  4. We get the text, present in the <textarea> , before the snippet name, and store it in beginValue. We get the text, present in the textarea, after the snippet name, and store it in endValue. We get the corresponding snippet body, and concatenate all three in this manner: beginValue + snip.body + endValue.
  5. We set the text of textarea to this new value we had got.
  6. But our caret remains at its original place, X characters after beginValue . We set the new position of the caret after snippet body, that is at (beginValue + snip.body).length .

Learning point:  Notice that I used for(var i = 0, len = Snippets.length; i < len; i++){  instead of the usual for(var i = 0; i < Snippets.length; i++){ . This is length caching. It involves storing the length of an array into a len variable, and then using this len variable in the condition. It saves the interpreter from computing array.length again and again and thus provides speed improvement while looping over large arrays. Note that, in case you delete/append an element from/to the array, be sure to update the len variable accordingly.

Congrats! You successfully understood and now know how to reciprocate the code required for this functioning of snippets. Yes, I know, you would definitely want to see whatever you did, live in action. So, here it is 🙂

Hope you enjoyed reading! Feel free to discuss anything in the comments below.

Next Post

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *