In this tutorial, you will learn to create your own online collaboration text editor with realtime collaboration features.
This blog post was written under the Pusher Guest Writer program.
In this post we’ll learn to create an awesome, tweet-worthy Online Collaborative Text Editor . You can now collaborate realtime with your friends on an editor created by you! This article is for all those ninjas out there who love building their own components. You will get a step by step guidance on building a collaborative text editor, so the next time you make a trip with your friends, or take notes in a class or want to jot down your awesome ideas collectively you can do all of that in your own shiny doc!
This is a glimpse of what we will be building by the end of this post.
Try the Online Collaborative Text Editor for yourself. Open this link, and share with your friends and Whoaaaaa! Collaborate away!
1<!DOCTYPE> 2<html> 3 <head> 4 <meta charset="utf-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0"> 6 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 7 <title>Online Collaborative Text Editor</title> 8 <link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet"> 9 <link href="/css/app.css" rel="stylesheet"></link> 10 </head> 11 <body> 12 <header class="header"> 13 <h1 class="header__h1">Online Collab Edit</h1> 14 </header> 15 <div class="doc"> 16 <div class="doc__background-ribbon"></div> 17 <div class="doc__text-editor hidden"></div> 18 </div> 19 </body> 20</html>
js
directory, app.js, and add the following three lines to see the magic!1var doc = document.getElementById('doc'); 2doc.contentEditable = true; 3doc.focus();
1var id = getUrlParameter('id'); 2 if (!id) { 3 location.search = location.search 4 ? '&id=' + getUniqueId() : 'id=' + getUniqueId(); 5 return; 6 }
getUrlParameter()
and getUniqueId()
are two utility functions that we defined to help us get query params and generate unique keys1// a unique random key generator 2 function getUniqueId () { 3 return 'private-' + Math.random().toString(36).substr(2, 9); 4 } 5 6 // function to get a query param's value 7 function getUrlParameter(name) { 8 name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]'); 9 var regex = new RegExp('[\\?&]' + name + '=([^&#]*)'); 10 var results = regex.exec(location.search); 11 return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' ')); 12 };
app-name
and choosing a cluster
in the Create App screenpusher's JavaScript library
in your index.html
1<script src="https://js.pusher.com/4.0/pusher.min.js"></script>
Pusher
constructor with your app key
as shown in the below line1var pusher = new Pusher(<INSERT_PUSHER_APP_KEY_HERE>);
channels
. Every new document will be a new channel
for us. Channel is represented by a string
, in our case it’ll be the unique Id we generated above. Read more about the awesome channels here.1// subscribe to the changes via Pusher 2var channel = pusher.subscribe(id); 3channel.bind('some-fun-event', function(data) { 4 // do something meaningful with the data here 5});
Client Events
in your Settings
tab on Pusher’s DashboardClient Events
should start with client-
, and thus our event name client-text-edit. (Note that Client Events
have a number of restrictions that are important to know about while creating your awesome app. Read about them here.)1channel.bind('client-text-edit', function(html) { 2 doc.innerHTML = html; 3});
1function triggerChange (e) { 2 channel.trigger('client-text-edit', e.target.innerHTML); 3} 4 5doc.addEventListener('input', triggerChange);
Promise
as you can only trigger changes to the channel when you’ve successfully subscribed to the channel itself!demo app
doesn’t account for cases like concurrent edits at the same place in the document. For a production-like app you’d want to use Operational Transformation to solve the problem.1git clone git@github.com:ankeetmaini/collaborative-text-editor.git 2 cd collaborative-text-editor 3 npm install 4 npm start