Categories
JavaScript JavaScript Basics

Lodash Features that are Available in Plain JavaScript

In recent years, new features in JavaScript have been rolling out at a rapid pace. The deficiencies that are filled in by other libraries before have become built-in features of plain JavaScript.

In this article, we’ll look at the methods in Lodash that are now available in plain JavaScript.

Map

Lodash has a map method to map each array entry to another by a function that we specify. It takes in an array and a function to transform the entries as arguments.

For example, we can call Lodash’s map method by writing:

const arr = _.map([1, 2, 3], n => n ** 2);

Then we get back:

[1, 4, 9]

JavaScript’s arrays now have the map method. We can use it as follows:

const arr = [1, 2, 3].map(n => n ** 2);

It also returns a new array but takes one less argument since it’s a method in an array.

Filter

The filter method in Lodash returns a new array that has the filtered entries of the original array. It takes an array and a function that returns the condition for the filter as arguments.

For example, we can write:

const arr = _.filter([1, 2, 3], n => n % 2 === 0);

to return an array with only even numbers.

With JavaScript array’s filter method, we can simplify the code a bit since it’s called on the array itself. We can rewrite it as:

const arr = [1, 2, 3].filter(n => n % 2 === 0);

In both cases, we get [2] back.

Reduce

Like the other 2 array methods, Lodash’s reduce method now has a plain JavaScript equivalent with the same name. Both of them let us combine our array entries into one thing.

For example, to get the sum of all the entries in an array, we can use the Lodash reduce method as follows:

const arr = _.reduce([1, 2, 3], (total, n) => total + n, 0);

The Lodash version took 3 arguments, which is the array to combine, the function used to combine the values, and the initial value for the combined value.

With JavaScript array’s reduce method, it’s similar except that we call it on the array directly as follows:

const arr = [1, 2, 3].reduce((total, n) => total + n, 0);

In both cases, we get 6 as a result.

Head

Lodash’s head method returns the first element of an array. For example:

const first = _.head([3, 4, 5]);

will get us 3 as the value of first .

We can do this with JavaScript by writing the following:

[3, 4, 5][0]

or:

const [first] = [3, 4, 5];

Tail

The Lodash’s tail method will get us everything but the first entry of an array.

For example, we can use it as follows:

const rest = _.tail([3, 4, 5]);

Then we get [4, 5] as the value of rest.

We can write the following to get everything but the first element of an array by writing:

const rest = [3, 4, 5].slice(1);

or:

const [first, ...rest] = [3, 4, 5];

The spread operator will give us the rest of the elements stored in the rest variable as the array, so we get the same thing.

Rest

Lodash’s rest method gets the extra arguments that are passed into the callback function that we pass into it that are in excess of the number of parameters and returns a function that let us do something with those extra arguments.

For example, if we have:

const restFn = _.rest((first, rest) => rest);

Then calling restFn(“foo”, “bar”, “baz”); will return [“bar”, “baz”] .

We have the rest operator to do the same thing in JavaScript’s rest operator:

const restFn = (first, ...rest) => rest

Spread

In Lodash, the spread method will return a function that takes a callback function, and then return a function where we pass in an array which we use as the arguments of the callback function we passed in.

For example, if we have:

const spreadFn = _.spread((foo, bar) => `${foo} ${bar}`);

Then when we call spreadFn as follows:

spreadFn(["foo", "bar", "baz"])

We get back:

'foo bar'

It’ll take each entry of the array passed into spreadFn , and pass it as the arguments of the function that we passed into the spread method.

This is the same as calling a function as is in JavaScript:

const spreadFn = function(foo, bar) {  
  return `${foo} ${bar}`;  
}

Then we can use the apply method on the function to do the same thing:

spreadFn.apply(null, ["foo", "bar", "baz"])

And we get the same result.

As we can see, lots of methods in Lodash have equivalents in plain JavaScript, especially array methods. So to reduce the reliance of libraries, we can use more plain JavaScript replacements here to replace existing uses of Lodash methods when they make code cleaner and lighter.

Categories
Angular JavaScript

How to Add Rich Text Editor to an Angular App with Localization

Adding a rich text editor to your Angular app is easy since there are libraries to do so already. It supports lots of features like changing fonts, adding or removing underline, adding lists, bolding text, changing default fonts, add pictures, adding placeholders, etc. Almost anything you can think of can be added to an editor. CKEditor is the rich text editor with the most comprehensive options available.

To use CKEditor in our Angular app, we use an Angular version of the editor available at https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/frameworks/angular.html, available as a Node package. This allows us to bind our input content to our model without writing code to do that ourselves.

We install the package by running:

npm i ng2-ckeditor

Then we include the following in the imports array of your module:

import { CKEditorModule } from 'ng2-ckeditor';  
import { FormsModule } from '@angular/forms';  
   
@NgModule({  
  // ...  
  imports: [CKEditorModule, FormsModule],  
  // ...  
})  
export class AppModule {}

In index.html , we add:

<script src="https://cdn.ckeditor.com/4.5.11/full-all/ckeditor.js"></script>

This includes all the plugins for CKEditor.

Then we add the following to our component, we add:

import { Component } from '@angular/core';  
   
@Component({  
  selector: 'sample',  
  template: `  
  <ckeditor  
    [(ngModel)]="content"  
    [config]="config"  
    [readonly]="false"  
    (change)="onChange($event)"  
    (editorChange)="onEditorChange($event)"   
    (ready)="onReady($event)"  
    (focus)="onFocus($event)"  
    (blur)="onBlur($event)"  
    (contentDom)="onContentDom($event)"  
    (fileUploadRequest)="onFileUploadRequest($event)"  
    (fileUploadResponse)="onFileUploadResponse($event)"  
    (paste)="onPaste($event)"  
    (drop)="onDrop($event)"  
    debounce="500">  
  </ckeditor>  
  `,  
})  
export class Editor{  
  content: string = '<p>Some html</p>'; 
  config: any = {  
    allowedContent: true,  
    toolbar: [['Bold', 'Italic', 'Underline', '-', 'NumberedList', 'BulletedList', 'Link', '-', 'CreatePlaceholder']],  
    removePlugins: 'elementspath',  
    resize_enabled: false,  
    extraPlugins: 'font,divarea,placeholder',  
    contentsCss: ["body {font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;}"],  
    autoParagraph: false,  
    enterMode: 2  
  };  
}

In the config , we define the options that are displayed in the editor. We can define the buttons shown in the toolbar with the toolbar property. We put all the options in the array. Full list of controls that you can display in the toolbar is at https://ckeditor.com/latest/samples/old/toolbar/toolbar.html. We have one array per toolbar row. We can also remove plugins that we don’t need with the removePlugins option, and add more plugins with the extraPlugins options. Plugins have to be installed separately if the full installation is not used like I did above.

It can handle model changes with the change handler. You can run code when the editor is loaded with the ready event. paste handler allows you to run code after something is pasted.

Now to change the language of the editor, we can add the language option to the config object. Use the standard language codes listed at https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes. For example, to set the language to Spanish, we put the following in the config object:

language: 'es',

It is very simple.

Another thing you might want to do with your editor is to customize the content of some of the elements. To do this, we can make an Angular directive and add some event handlers to the CKEDITOR object to set the content you want. For example, if you want to set labels of the inputs of the Placeholder plugin that we have above, we run:

ng g directive changeEditorOptions

Then we get:

import { Directive } from '@angular/core';  
@Directive({  
  selector: '[appChangeEditorOptions]'  
})  
export class ChangeEditorOptionsDirective { 
  constructor() {}
}

Next, we reference the CKEDITOR global object we got from putting <script src=”https://cdn.ckeditor.com/4.5.11/full-all/ckeditor.js"></script> in index.html and hook some event handlers to the CKEDITOR object and put that into the constructor. After that we get the following:

import { Directive } from '@angular/core';  
  
declare let CKEDITOR: any;  
declare let $: any;
@Directive({  
  selector: '[appChangeEditorOptions]'  
})  
export class ChangeEditorOptionsDirective{ 

  constructor() { 
    CKEDITOR.on(  
      'instanceReady',  
      (ev) => {  
  
      }  
    ); 

    CKEDITOR.on('dialogDefinition', (event) => {  
      if ('placeholder' === event.data.name) {  
  
      }  
    }); 
}}

instanceReady event is fired when the editor is loaded, and dialogDefinition is loaded when the user opens a dialog box from the editor.

To add a drop down to our Placeholders dialog box and change the placeholders available in the placeholder plugin we can add it into the dialogDefinition event handler. We can make our changes by adding the following:

if ('placeholder' === event.data.name) {  
  const input = event.data.definition.getContents('info').get('name');  
  const dialog = event.data.definition;  
  input.type = 'select';  
  input.items = [  
    ['First Name', 'first_name'],  
    ['Last Name', 'last_name'],  
    ['Link', 'link'],  
  ];}

The code works like this: after opening the dialog, we get the name element in the info section by calling event.data.definition.getContents(‘info’).get(‘name’); Then we change the input to select by setting that to input.type and we populate the input by assigning the array to input.items . The first item in the array is what is displayed and the second is the value.

At the end, we have:

import { Directive } from '@angular/core';  
  
declare let CKEDITOR: any;  
declare let $: any;

@Directive({  
  selector: '[appChangeEditorOptions]'  
})  
export class ChangeEditorOptionsDirective{ 
  constructor() { 
    CKEDITOR.on(  
      'instanceReady',  
      (ev) => {  
  
      }  
    ); 

    CKEDITOR.on('dialogDefinition', (event) => {  
      if ('placeholder' === event.data.name) {  
        const input = event.data.definition.getContents('info').get('name');  
        const dialog = event.data.definition;  
        input.type = 'select';  
        input.items = [  
          ['First Name', 'first_name'],  
          ['Last Name', 'last_name'],  
          ['Link', 'link'],  
        ];  
      }  
    }); }  
}

To run script with the editor loads, we put the following in the instanceReady handler:

const $script = document.createElement('script'),  
$editor_instance = CKEDITOR.instances[ev.editor.name];
$script.src = '//path/to/your/script';  
$script.onload = () => {  
  //run code after load  
}};

$editor_instance.document.getHead().$.appendChild($script);

Now we have:

import { Directive } from '@angular/core';  
  
declare const CKEDITOR: any;
@Directive({  
  selector: '[appChangeEditorOptions]'  
})  
export class ChangeEditorOptionsDirective{ 
  constructor() { 
    CKEDITOR.on(  
      'instanceReady',  
      (ev) => {  
         const $script = document.createElement('script'),  
         $editor_instance = CKEDITOR.instances[ev.editor.name]; 
         $script.src = '//path/to/your/script';  
         $script.onload = () => {  
           //run code after load  
         }}; $editor_instance.document.getHead().$.appendChild($script);  
      }  
    ); 

    CKEDITOR.on('dialogDefinition', (event) => {  
      if ('placeholder' === event.data.name) {  
        const input = event.data.definition.getContents('info').get('name');  
        const dialog = event.data.definition;  
        input.type = 'select';  
        input.items = [  
          ['First Name', 'first_name'],  
          ['Last Name', 'last_name'],  
          ['Link', 'link'],  
        ];  
      }  
    }); }  
}

Since CKEDITOR is global, we need to put declare keyword before it so that the compiler won’t think it’s undefined.

Apply this directive to your editor by adding:

import { Component } from '@angular/core';  
   
@Component({  
  selector: 'sample',  
  template: `  
  <div appChangeEditorOptions>    
    <ckeditor  
      [(ngModel)]="content"  
      [config]="config"  
      [readonly]="false"  
      (change)="onChange($event)"  
      (editorChange)="onEditorChange($event)"   
      (ready)="onReady($event)"  
      (focus)="onFocus($event)"  
      (blur)="onBlur($event)"  
      (fileUploadRequest)="onFileUploadRequest($event)"  
      (fileUploadResponse)="onFileUploadResponse($event)"  
      (paste)="onPaste($event)"  
      (drop)="onDrop($event)"  
      debounce="500">  
    </ckeditor>  
  </div>  
  `,  
})  
export class Editor {  
  content: string = '<p>Some html</p>';
  config: any = {  
    allowedContent: true,  
    toolbar: [['Bold', 'Italic', 'Underline', '-', 'NumberedList', 'BulletedList', 'Link', '-', 'CreatePlaceholder']],  
    removePlugins: 'elementspath',  
    resize_enabled: false,  
    extraPlugins: 'font,divarea,placeholder',  
    contentsCss: ["body {font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;}"],  
    autoParagraph: false,  
    enterMode: 2  
  };  
}

Also include, ChangeEditorOptionsDirective in the app.module.ts into the declaration section of the module if it’s not there already.

Categories
Angular JavaScript

Defining and Using Angular Attribute Directives

Angular is a popular front-end framework made by Google. Like other popular front-end frameworks, it uses a component-based architecture to structure apps.

In this article, we’ll look at how to create and use attribute directives with Angular.

Build a Simple Attribute Directive

We can create a simple directive by using the Angular CLI.

To create one, we run:

ng generate directive highlight

to create the skeleton code for our directive.

Once we did that, we can write some code for our directive. We write the following code:

highlight.directive.ts :

import { Directive, ElementRef } from '@angular/core';

@Directive({  
  selector: '[appHighlight]'  
})  
export class HighlightDirective {  
  constructor(el: ElementRef) {  
    el.nativeElement.style.backgroundColor = 'pink';  
  }  
}

The code above takes an ElementRef of the element that the directive is applied to.

Then we set the backgroundColor of it to 'pink' .

The bracket makes our directive an attribute directive.

We should prefix our directive names with something like app so that they don’t clash with HTML attribute names.

app.module.ts :

import { BrowserModule } from '@angular/platform-browser';  
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';  
import { AppComponent } from './app.component';  
import { HighlightDirective } from './highlight.directive';

@NgModule({  
  declarations: [  
    AppComponent,  
    HighlightDirective  
  ],  
  imports: [  
    BrowserModule,  
    AppRoutingModule  
  ],  
  providers: [],  
  bootstrap: [AppComponent]  
})  
export class AppModule { }

We added HighlightDirective to our AppModule so we can use it.

Then we can use it in app.component.html as follows:

<div appHighlight>foo</div>

We then should see a pink div with ‘foo’ inside it.

Respond to User-Initiated Events

We can also use directives to detect and act on user-initialed events.

For example, we can write the change the HighlightDirective we had above as follows:

import { Directive, ElementRef, HostListener } from '@angular/core';

@Directive({  
  selector: '[appHighlight]'  
})  
export class HighlightDirective {  
  constructor(private el: ElementRef) {    } 

  @HostListener('mouseenter') onMouseEnter() {  
    this.highlight('pink');  
  } 

  @HostListener('mouseleave') onMouseLeave() {  
    this.highlight(null);  
  } 

  private highlight(color: string){  
    this.el.nativeElement.style.color = color;  
  }  
}

We added the HostListener decorators to listen to the mouseenter and mouseleave events of the element that our highlight directive is applied to.

Therefore, when we hover over our text, we’ll see it turn pink. Otherwise, it’ll go back to its normal color.

Pass Values Into the Directive with an @Input Data Binding

We can use the @Input decorator as we do with components to pass data to directives.

For example, we can write the following:

highlight.directive.ts :

import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({  
  selector: '[appHighlight]'  
})  
export class HighlightDirective {  
  @Input() highlightColor: string;  
  constructor(private el: ElementRef) { } 

  ngOnInit() {  
    this.el.nativeElement.style.color = this.highlightColor;  
  }  
}

app.component.html :

<div appHighlight highlightColor='pink'>foo</div>

In the code above, we added the highlightColor input and then pass in the color string in app.component.html to set the color.

We’ll then get pink text since we set highlightColor to pink.

Bind to an @Input Alias

We can make the code above shorter if we change the @Input alias to have the same name as the directive name.

For example, we can write:

highlight.directive.ts :

import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({  
  selector: '[appHighlight]'  
})  
export class HighlightDirective {  
  @Input('appHighlight') highlightColor: string;  
  constructor(private el: ElementRef) { } 

  ngOnInit() {  
    this.el.nativeElement.style.color = this.highlightColor;  
  }  
}

Then we can use it as follows in app.component.html ;

<div appHighlight='pink'>foo</div>

As we can see, we don’t have to specify highlightColor separately anymore.

Bind to a Second Property

We can have multiple @Input s to bind to multiple properties.

For example, we can write the following:

highlight.directive.ts :

import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({  
  selector: '[appHighlight]'  
})  
export class HighlightDirective {  
  @Input('appHighlight') highlightColor: string;  
  @Input() defaultColor: string;  
  constructor(private el: ElementRef) { } 

  ngOnInit() {  
    this.el.nativeElement.style.color = this.highlightColor || this.defaultColor;  
  }  
}

In the code above, we have 2 @Inputs instead of one.

Now we can use it as follows in app.component.html :

<div appHighlight='pink' defaultColor='yellow'>foo</div>  
<div appHighlight defaultColor='green'>bar</div>

In the code above, we have appHighlight directive applied without any color string set in the second div.

Therefore, since defaultColor is set to 'green' , it’ll be applied as we specified in highlight.directive.ts ‘s ngOnInit method.

Conclusion

We define attribute directives by running ng g directive and name them with brackets outside the name.

Then we can pass in properties with @Input s and listen to events with @HostListener .

Also, we get the element that the directive is applied to and then apply whatever styles we want to it.

Categories
Angular JavaScript

Angular Reactive Forms — Patching Values and Building Forms

Angular is a popular front-end framework made by Google. Like other popular front-end frameworks, it uses a component-based architecture to structure apps.

In this article, we’ll look at how to update values in the form group and use FormBuilder .

Patching the Model Value

There’re 2 ways to update the model value of Reactive forms.

We can either call setValue on an individual control to update the value or call patchValue on a FormGroup to replace values in the form model.

We can call setValue as follows:

app.component.ts :

import { Component } from "@angular/core";  
import { FormControl, FormGroup } from "@angular/forms";

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {  
  personForm = new FormGroup({  
    name: new FormControl(""),  
    address: new FormGroup({  
      street: new FormControl("")  
    })  
  }); 

  updateName() {  
    this.personForm.setValue({ ...this.personForm.value, name: "Jane" });  
  }  
}

app.component.html :

<form [formGroup]="personForm">  
  <input type="text" formControlName="name" placeholder="Name" />  
  <div formGroupName="address">  
    <input type="text" formControlName="street" placeholder="Street" />  
  </div>  
</form><button (click)="updateName()">Update Name</button>

setValue strictly adheres to the structure of the form group, so we have to include everything as we did in the updateName method.

If we only want to update some values, we can use the patchValue method as follows:

app.component.ts :

import { Component } from "@angular/core";  
import { FormControl, FormGroup } from "@angular/forms";

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {  
  personForm = new FormGroup({  
    name: new FormControl(""),  
    address: new FormGroup({  
      street: new FormControl("")  
    })  
  }); 

  updateName() {  
    this.personForm.patchValue({ name: "Jane" });  
  }  
}

app.component.html :

<form [formGroup]="personForm">  
  <input type="text" formControlName="name" placeholder="Name" />  
  <div formGroupName="address">  
    <input type="text" formControlName="street" placeholder="Street" />  
  </div>  
</form><button (click)="updateName()">Update Name</button>

In the code above, the updateName method changed to:

this.personForm.patchValue({ name: "Jane" });

As we can see, we don’t have to include all the fields. We only included the ones we want to update.

Generating Form Controls with FormBuilder

We can use the FormBuilder class to make creating Reactive forms easier for us.

To create a form with FormBuilder , we can write the following:

app.component.ts :

import { Component } from "@angular/core";  
import { FormBuilder } from "@angular/forms";

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {  
  constructor(private fb: FormBuilder) {} 
  personForm = this.fb.group({  
    name: [""],  
    address: this.fb.group({  
      street: [""]  
    })  
  });  
}

app.component.html :

<form [formGroup]="personForm">  
  <input type="text" formControlName="name" placeholder="Name" />  
  <div formGroupName="address">  
    <input type="text" formControlName="street" placeholder="Street" />  
  </div>  
</form>

The code above has the same functionality as the other examples.

We have a personForm with the name field in the root form group and the street field nested in the address form group.

Simple Form Validation

To make sure what’s inputted if complete and correct, we have to add form validation to our form.

With Reactive forms, we can ads a single validator to our form control and also display the overall form status.

We can add validation as follows:

app.component.ts :

import { Component } from "@angular/core";  
import { FormBuilder, Validators } from "@angular/forms";

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {  
  constructor(private fb: FormBuilder) {} 
  personForm = this.fb.group({  
    name: ["", Validators.required],  
    address: this.fb.group({  
      street: ["", Validators.required]  
    })  
  });  
}

app.component.html :

<form [formGroup]="personForm">  
  <input type="text" formControlName="name" placeholder="Name" required />  
  <div formGroupName="address">  
    <input type="text" formControlName="street" placeholder="Street" required />  
  </div>  
</form>

In the code above, we made the name and street fields required by importing the Validator and using the required property in each field.

Also, we have to add the required attribute in the inputs to prevent errors when the expression has changed after the template has been checked.

Displaying Form Status

We can reference the status property of our form to get the validator status of our form.

To do this, we just change app.component.html to the following:

<form [formGroup]="personForm">  
  <p>Form Status: {{ personForm.status }}</p>  
  <input type="text" formControlName="name" placeholder="Name" required />  
  <div formGroupName="address">  
    <input type="text" formControlName="street" placeholder="Street" required />  
  </div>  
</form>

Now the form should show the validation status of it. personForm.status returns 'INVALID' if it’s not valid and 'VALID' if it’s valid.

Therefore, we should see that in the first line of the form.

Dynamic Controls Using Form Arrays

To add dynamic controls, we can use the FormArray class.

We can use FormArray as follows:

app.component.ts :

import { Component } from "@angular/core";  
import { FormBuilder, FormArray } from "@angular/forms";

@Componen({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {  
  constructor(private fb: FormBuilder) {}  
  personForm = this.fb.group({  
    names: this.fb.array([this.fb.control("")])  
  }); 

  get names() {  
    return this.personForm.get("names") as FormArray;  
  } 

  addName() {  
    this.names.push(this.fb.control(""));  
  }  
}

app.component.html :

<form [formGroup]="personForm">  
  <div formArrayName="names">  
    <h3>Names</h3>  
    <button (click)="addName()">Add Name</button>  
    <div *ngFor="let n of names.controls; let i=index">  
      <label>  
        Name:  
        <input type="text" [formControlName]="i" />  
      </label>  
    </div>  
  </div>  
</form>

In the code above, we created the names form array in personForm .

Then in the template, we loop through the controls with the *ngFor in the form.

The formControlName is set to the index of the form control.

We also added an Add Name button to add a new form control.

Conclusion

We can use the FormBuilder class to save us time when we want to build forms.

It’s also useful for building dynamic forms since it takes FormArrays .

We can get the status of a form with the status property of the form group.

To update the values of the form, we can call the setValue or patchValue method of a form group.

Categories
JavaScript

JavaScript Events Handlers- onreadystatechange and onselectionchange

In JavaScript, events are actions that happen in an app. They’re triggered by various things like inputs being entered, forms being submitted, and changes in an element like resizing, or errors that happen when an app is running, etc. We can assign an event handler to handle these events.

Events that happen to DOM elements can be handled by assigning an event handler to properties of the DOM object for the corresponding events.

In this article, we will look at the onreadystatechange and the onselectionchange event handlers.

window.document.onreadystatechange

The onreadystatechange lets us attach an event handler to handle the readystatechange event. The readyState is changed as our page loads.

The event will be trigger when the document.readyState changes values, which can be one of 3 values, which is loading , which means that the document is loading, interactive , which means that the document has finished loading and the document has been parsed but sub-resources like images, stylesheets and frames are still loading, or complete , which means that the document and sub-resources have loaded.

For example, we can write the code to get the readyState of document as the page is loading:

document.onreadystatechange = () => {  
  console.log(document.readyState);  
};

If we run the code above, we should get something like:

interactive  
complete

from the console.log output.

We can look at the change of the readyState as the page loads with the code above. To look at it more slowly, we can go to the Network tab of Chrome and change the speed of that the page loads with the drop-down on the top of the tab. We can choose Fast 3G, Slow 3G, or choose ‘Add…’ to set our own speed.

window.document.onselectionchange

The onselectionchange property let us attach an event handler function to process the selectionchange event. The selectionchange event is fired when the current text selection on a document is changed. For example, we can use it like in the following code:

document.onselectionchange = () => {  
  console.log(document.getSelection());  
}

With the code above, we’re logging the Selection object, which represents the text that was selected by the user or the current position of the caret.

A user may make the selection from left to right or right to left. The anchor is where the user starts the selection and the focus is where the user ends the selection.

If the mouse is used for the selection then the anchor is placed where we place the mouse button and the focus is placed where we release the mouse button. The Selection object has the following value properties:

  • anchorNode — is a read-only that returns the Node in which the selection begins. It can return null if selection never existed in the document, for example, a page that was never clicked on will have no selection.
  • anchorOffset — is a read-only that returns a number representing the offset of the selection’s anchorNode within the anchor. If anchorNode is a text node, this is the number of characters within anchor preceding the anchor. If anchorNode is an element, this is the number of child nodes of the anchor preceding the anchor. The anchorNode property is a read-only property that has the Node is which the selection begins. It’ll have the data , textContent , and wholeText properties to get the data DOM Node that’s at the start of the selection.
  • focusNode — is a read-only that returns the node in which the selection ends. It can return null if selection never existed in the document, for example, a page that was never clicked on. It’ll have the data , textContent , and wholeText properties to get the data DOM Node that’s at the end of the selection.
  • focusOffset — is a read-only that returns a number representing the offset of the selection’s anchor within the focus node. If the focus node is a text node, this is the number of characters within the focus node preceding the focus. If focus node is an element, this is the number of child nodes of the focus node preceding the focus.
  • isCollapsed — is a read-only that returns a boolean that indicates whether the selection’s start and endpoints are in the same position.
  • rangeCount — is a read-only that returns the number of ranges in the selection.
  • type — is a read-only that returns a string describing the type of the current selection.

Also, it has a toString method that’ll get us the actual selected text. For example, we can use it below like in the code below:

document.onselectionchange = () => {  
  console.log(document.getSelection().toString());  
}

Then if we have the following HTML code:

<p>  
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque fermentum erat viverra odio condimentum, vitae feugiat ex condimentum. Maecenas convallis tempus nisl consequat mollis. Nullam tempus ultrices est id accumsan. Duis in dignissim magna. Sed rutrum ac metus ac faucibus. Proin ut ex felis. Donec malesuada eget neque a egestas. Donec vitae aliquet tellus. Suspendisse in egestas nisi. In consequat metus id enim bibendum faucibus. Praesent hendrerit, leo vitae sollicitudin sollicitudin, tellus ante ornare est, nec laoreet augue sem et orci. Etiam tempor pretium faucibus. Etiam eros mauris, hendrerit sed molestie a, eleifend sed eros. Etiam efficitur congue euismod.</p><p>  
  Quisque viverra nibh a ornare fermentum. In viverra, orci sed mattis sodales, nisl eros fermentum lectus, id lobortis ligula diam eu sem. Nulla in tincidunt enim. Donec suscipit tortor ac aliquet pharetra. Donec lobortis nibh in turpis dignissim dapibus. Nunc eu erat quis dui fermentum convallis. Fusce posuere, augue vitae mattis viverra, est velit finibus tellus, non dictum tortor dolor sit amet felis. Aliquam auctor risus urna, non posuere magna dapibus a. Vivamus iaculis lorem massa, eget mollis libero consectetur et. Integer iaculis lorem eget turpis accumsan, nec vulputate sapien accumsan. Donec facilisis arcu nec nisi posuere finibus vel non augue. Donec tempor tortor ac mauris efficitur, nec sollicitudin nisl commodo. Proin tincidunt odio ac turpis bibendum, sed aliquet metus fermentum. In hac habitasse platea dictumst.</p>

We get something like the following logged with the event handler:

us nisl consequat mollis. Nullam tempu

The onreadystatechange gets us the state of the page when it’s loading. It’s handy if we want to do something when the page is loading.

The onselectionchange event handlers let us process the selectchange event, which is triggered when users make selections on the page by highlighting content.

We can get the anchor and focus nodes for the selection inside the event handler by using the document.getSelection() method, and we can get the selected text by calling the toString() method on document.getSelection() .