Categories
JavaScript

JavaScript Events Handlers — Metadata and Ajax Events

In JavaScript, events are actions that happen in an app. They are triggered by various things like inputs being entered, forms being submitted, changes in an element like resizing, or errors that happen when an app is running, etc. We can assign an event handler to take action on 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 onloadedmetadata property of a media DOM element, and the onloadend property of an XmlHttpRequest object.

onloadedmetadata

The onloadedmetadata property of a media DOM element lets us set an event handler function that’s run when the loademetadata event is fired. Whenever media metadata is loaded this event will fire.

For example, we can use it by first adding a video tag to our HTML code:

<video src='https://sample-videos.com/video123/mp4/240/big_buck_bunny_240p_30mb.mp4'></video>

Then in the corresponding JavaScript code, we can set an event handler function to the onloadedmetadata property as we do in the following code:

const video = document.querySelector('video');
video.onloadedmetadata = (e) => {  
  console.log(e);   
}

Then we get the video’s metadata in the srcElement property of the e parameter, which is an Event object. We get something like:

clientHeight: 240  
clientLeft: 0  
clientTop: 0  
clientWidth: 320  
contentEditable: "inherit"  
controls: false  
controlsList: DOMTokenList [value: ""]  
crossOrigin: null  
currentSrc: "https://sample-videos.com/video123/mp4/240/big_buck_bunny_240p_30mb.mp4"  
currentTime: 0  
dataset: DOMStringMap {}  
defaultMuted: false  
defaultPlaybackRate: 1  
dir: ""  
disablePictureInPicture: false  
disableRemotePlayback: false  
draggable: false  
duration: 368.2  
elementTiming: ""  
ended: false  
enterKeyHint: ""  
error: null  
firstChild: null  
firstElementChild: null  
height: 0  
hidden: false  
id: ""  
innerHTML: ""  
innerText: ""  
inputMode: ""  
isConnected: true  
isContentEditable: false  
lang: ""  
lastChild: null  
lastElementChild: null  
localName: "video"  
loop: false  
mediaKeys: null  
muted: false  
namespaceURI: "[http://www.w3.org/1999/xhtml](http://www.w3.org/1999/xhtml)"  
networkState: 1  
nextElementSibling: script  
nextSibling: text  
nodeName: "VIDEO"  
nodeType: 1  
nodeValue: null  
nonce: ""  
offsetHeight: 240  
offsetLeft: 8  
offsetParent: body  
offsetTop: 8  
offsetWidth: 320  
...  
outerHTML: "<video src="https://sample-videos.com/video123/mp4/240/big_buck_bunny_240p_30mb.mp4"></video>"  
outerText: ""  
ownerDocument: document  
parentElement: body  
parentNode: body  
part: DOMTokenList [value: ""]  
paused: true  
playbackRate: 1  
played: TimeRanges {length: 0}  
playsInline: false  
poster: ""  
prefix: null  
preload: "metadata"  
previousElementSibling: null  
previousSibling: text  
readyState: 4  
remote: RemotePlayback {state: "disconnected", onconnecting: null, onconnect: null, ondisconnect: null}  
scrollHeight: 240  
scrollLeft: 0  
scrollTop: 0  
scrollWidth: 320  
seekable: TimeRanges {length: 1}  
seeking: false  
shadowRoot: null  
sinkId: ""  
slot: ""  
spellcheck: true  
src: "https://sample-videos.com/video123/mp4/240/big_buck_bunny_240p_30mb.mp4"  
srcObject: null  
style: CSSStyleDeclaration {alignContent: "", alignItems: "", alignSelf: "", alignmentBaseline: "", all: "", …}  
tabIndex: -1  
tagName: "VIDEO"  
textContent: ""  
textTracks: TextTrackList {length: 0, onchange: null, onaddtrack: null, onremovetrack: null}  
title: ""  
translate: true  
videoHeight: 240  
videoWidth: 320  
volume: 1  
webkitAudioDecodedByteCount: 10995  
webkitDecodedFrameCount: 4  
webkitDisplayingFullscreen: false  
webkitDroppedFrameCount: 0  
webkitSupportsFullscreen: true  
webkitVideoDecodedByteCount: 37638

Some useful metadata include the videoHeight, which tells us the height of the video in pixels, videoWidth, which tells us the width of the video in pixels, and duration which tells us the length of the video in seconds. duration is also available audio elements.

onloadend

The onloadend property of an XMLHttpRequest object let us assign an event handler to it which is run whenever the loadend event is fired. The loadend event is fired whenever a request is completed, regardless of whether the request is completed successfully or not. If it’s successful then this event will be fired after the load event. Otherwise, it’ll fire after the abort or error events.

For example, we can assign an event handler to the onloadend event as we do in the following code:

const loadButtonSuccess = document.querySelector('.load.success');  
const loadButtonError = document.querySelector('.load.error');  
const loadButtonAbort = document.querySelector('.load.abort');  
const log = document.querySelector('.event-log');

function handleLoadEnd(e) {  
    log.textContent = log.textContent + `${e.type}: ${e.loaded} bytes transferred\n`;  
}

function addListeners(xhr) {  
  xhr.onloadend = handleLoadEnd;  
}

function get(url) {  
  log.textContent = ''; const xhr = new XMLHttpRequest();  
  addListeners(xhr);  
  xhr.open("GET", url);  
  xhr.send();  
  return xhr;    
}

loadButtonSuccess.addEventListener('click', () => {  
    get('https://jsonplaceholder.typicode.com/todos/1');  
});

loadButtonError.addEventListener('click', () => {  
    get('https://somewhere.org/i-dont-exist'));  
});

loadButtonAbort.addEventListener('click', () => {  
    get('https://jsonplaceholder.typicode.com/todos/1').abort();  
});

In the code above, we have 3 buttons that run the click event handler whenever each button is clicked. The ‘Load (success)’ button will run the get function when it’s clicked. We’ll pass in a valid URL in the call for the get function. The click handling for the ‘Load (success)’ button is done by the following block:

loadButtonSuccess.addEventListener('click', () => {  
    get('https://jsonplaceholder.typicode.com/todos/1');  
});

The JSONPlaceholder has a test API that can serve any URL since it hosts a fake API so we can load it and not get any errors. Likewise, we have buttons for load a URL that’ll give an error, and another button to load a valid URL but then we abort the request. Once the XmlHttpRequest is finished, then the function we assigned to the onloadend event handler, which is the handleLoadEnd function, will be run.

The handleLoadEnd function has one parameter, which is an Event object with some data about the request that’s finished. In the function, we get the value of the type property, which has the event type that’s fired, which should be loadend . Also, we get the value of the loaded property which has the number of bytes of data that’s been loaded.

Then in the HTML code, we add the elements listed in the querySelector calls above:

<div class="controls">  
    <input class="load success" type="button" name="xhr" value="Load (success)" />  
    <br>  
    <input class="load error" type="button" name="xhr" value="Load (error)" />  
    <br>  
    <input class="load abort" type="button" name="xhr" value="Load (abort)" />  
</div>
<textarea readonly class="event-log"></textarea>

We have 3 buttons to click on to load the successful HTTP request, an HTTP request with a non-existent URL, and an aborted HTTP request respectively. Then we display the event that’s fired and the number of bytes loaded.

The onloadedmetadata property of a media DOM element lets us set an event handler function that’s run when the loademetadata event is fired. Whenever media metadata is loaded this event will fire. We can get the metadata that’s loaded from the srcElement property of the event parameter of the event handler function.

The onloadend property of an XMLHttpRequest object let us assign an event handler to it which is run whenever the loadend event is fired. The loadend event is fired whenever a request is completed, regardless of whether the request is completed successfully or not. If it’s successful then this event will be fired after the load event. Otherwise, it’ll fire after the abort or error events. We can handle this event by setting the onloadend property of an XMLHttpRequest object. The event handler function should have an event parameter which is an event object that has the type property, which has the event type that’s fired, which should be loadend . Also, we get the value of the loaded property which has the number of bytes of data that’s been loaded.

Categories
Angular JavaScript

A guide to styling Angular Components

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 ways to style Angular components.

Component Styles

Angular components are styled with CSS. Everything about CSS applies to Angular components.

We can also compound component styles with components, which allows for more modular designs than regular stylesheets.

Using Component Styles

We can define CSS styles in addition to template content in our components.

One way to do it is to use the styles property in our component.

For example, we can write that as follows:

app.component.ts :

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

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styles: [  
    `  
      h1 {  
        font-size: 50px;  
      }  
    `  
  ]  
})  
export class AppComponent {}

app.component.html :

<h1>Foo</h1>

Then we should see ‘Foo’ that’s 50 pixels big on the screen.

Styles in styles isn’t inherited by any components nested within the template or any content projected into the component.

We can use CSS class names and selectors that make the most sense in each component.

Class names and selectors don’t collide with class names and selectors in other parts of the app.

Changes elsewhere in the app don’t affect changes in the current component.

We can co-locate the CSS code of each component with TypeScript and HTML code of the component, which makes the project structure cleaner.

Also, we can change or remove CSS code without searching through the whole app to find where else the code is used.

Special Selectors

Angular apps can use special selectors to style components.

They come from selectors for styling the shadow DOM.

:host

The :host pseudoclass selector targets styles in the element that hosts the component rather than the elements inside the component’s template.

For example, we can write:

:host {  
  display: block;  
  border: 3px solid black;  
}

We can style host styles with the given selector by using the function form as follows:

:host(.active) {  
  border-width: 1px;  
}

:host-context

We can use :host-context to apply styles on some condition outside of a component’s view.

It works the same way as the :host selector, which can use the function form.

It’ll look in ancestor components all the way up to the document root for the given selector.

For example, we can write:

:host-context(.theme-light) h2 {  
  background-color: #fff;  
}

/deep/, >>>, and ::ng-deep (Deprecated)

We can use /deep/, >>>, and ::ng-deep to apply styles to outside of the component by disabling view encapsulation for the rule.

For example, we can write:

:host /deep/ h3 {  
  font-weight: bold;  
}

Style Files in Component Metadata

We can set the styleUrls to point to a stylesheet file to style a component.

For example, we can write the following:

app.component.ts :

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

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {}

app.component.css :

h1 {  
  font-size: 50px;  
}

app.component.html ;

<h1>Foo</h1>

In the code above, we put our styles in app.component.css and point our styleUrls to that file in app.component.ts .

We can specify more than one style file in both styles and styleUrls .

Template Inline Styles

We can put style tags in our templates to add template inline styles.

For example, we can write:

app.component.html :

<style>  
  h1 {  
    font-size: 50px;  
  }  
</style>  
<h1>Foo</h1>

The code above will set the h1’s font size to 50 pixels.

Template Link Tags

We can add link tags to templates to reference other styles.

For example, we can write the following code:

app.component.ts :

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

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html"  
})  
export class AppComponent {}

app.component.html :

<link rel="stylesheet" href="./app.component.css" />  
<h1>Foo</h1>

We see that we removed the styleUrls from AppComponent and put a link tag to reference the CSS file.

The styles should be applied from the file we referenced in the link tag.

CSS @imports

We can import CSS files with the standard CSS @import rule.

For example, we can write the following code:

foo.css :

h1 {  
  font-size: 50px;  
}

app.component.css :

@import url("./foo.css");

app.component.html :

<h1>Foo</h1>

External and Global Style Files

We can add external global styles to angular.json to include them in the build.

To register a CSS file, we can put in the styles section, which is set to styles.css by default.

Non-CSS Style Files

Angular CLI supports building style files in SASS, LESS or Stylus.

We can specify those files in styleUrls with the appropriate extensions (.scss, .less, or .styl) as in the following example:

app.component.scss :

h1  {  
  font-size: 70px;  
}

app.component.html :

<h1>Foo</h1>

View Encapsulation

We can control how view encapsulation works by setting the encapsulation setting in out component code.

The following are the possibilities for view encapsulation:

  • ShadowDom view encapsulation uses the browser’s native shadow DOM implementation. The component’s styles are included in the Shadow DOM
  • Native view encapsulation uses the now deprecated version of the browser’s native shadow DOM implementation
  • Emulated is the default option. It emulates the behavior of the shadow DOM by preprocessing and renaming CSS code to effectively scope the CSS to the component’s view
  • None means that Angular does no view encapsulation. The styles are added to global styles.

We can change the encapsulation setting as follows:

app.component.ts :

import { Component, ViewEncapsulation } from "@angular/core";

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.scss"],  
  encapsulation: ViewEncapsulation.Native  
})  
export class AppComponent {}

Conclusion

We can use CSS to style Angular components. In addition, it supports SASS, LESS, and Stylus for styling.

By default, the scope of the styles is local to the component. However, we can change that to be different.

We can also include inline styles in our templates via the style and link tags.

Categories
JavaScript React

Introduction to React Hooks

React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.

In this article, we’ll look at how to use React hooks to make function components smarter.

Introducing Hooks

Hooks are introduced with React 16.8.

We can use hooks to make function components keep and change dynamic states.

For example, we can write the following code to add a hook to our app:

import React from "react";  
import ReactDOM from "react-dom";
function App() {  
  const [count, setCount] = React.useState(0); 
  return (  
    <div>  
      <button onClick={() => setCount(count + 1)}>Count {count}</button>  
    </div>  
  );  
}

In the code above, we have the React.useState hook with the default 0 passed in. It returns an array with the state, which we called count , and a function to set the state, which is called setCount .

Then in the onClick handler of the button, we can the setCount function to update the count.

Other packages like React DOM also has to be updated to 16.8 or later.

React Native since 0.59 also supports hooks.

No Breaking Changes

Hooks are completely opt-in. They coexist with class components. Therefore, we don’t have to change any existing code. They’re also completely interoperable with class components.

Class components will be available in the future. They also don’t replace our knowledge of existing React concepts.

Why do we need Hooks

We can use gooks to reuse stateful logic without creating higher-order components or doing other things to change our component hierarchy.

Complex Components Become Hard to Understand

Complex components have many methods which are lifecycle hooks and also our own user-defined functions.

They all have a mix of unrelated logic. This makes it too easy to introduce bugs and inconsistencies.

It’s impossible to break them into smaller components because stateful logic is everywhere. It’s also hard to test them because of the complexity.

We can use hooks to split one component into smaller functions based on what’s related.

Classes are Confusing

We have to understand a lot about how JavaScript classes work, which is different from most languages since JavaScript classes are just syntactic sugar on top of its own prototypical inheritance model.

Also, we have to make sure the value of this is correct so we don’t introduce errors.

Classes can introduce patterns that make optimizations difficult. They don’t minify well and they make hot reloading flaky and unreliable.

Hooks are used without classes, so we don’t have to worry about any of these issues.

Gradual Adoption Strategy

We can create new components with hooks without affecting anything existing code.

Therefore, we can try them on non-critical parts of an app first.

Class components will be supported in the foreseeable future.

Conclusion

Hooks are the new way to keep dynamic states in function components.

We can use function components with hooks the same way that we write class components.

They coexist with existing class components and they’ll still be supported in the foreseeable future.

Hooks remove the confusion that arises from JavaScript classes and also let React minify the code by providing additional optimizations that can’t be done with classes.

Categories
Express JavaScript

Compress Express.js Responses with the Compression Middleware

Compressing our responses is a great way to speed up our Express app’s performance.

To do this, we can use the compression middleware.

In this article, we’ll look at the options available to us for compressing data and how to send a compressed response with it.

Adding the Middleware

We can install the package by running:

npm install compression

Then we can include it with:

const compression = require('compression');

Options

The compression middleware takes an options parameter, which is an object that can have the following properties:

chunkSize

The chunk size of the compressed output. Default is 16384 bytes or zlib.Z_DEFAULT_CHUNK , where zlib is the zlib Node package.

filter

A function that decides what kind of responses should be compressed. The function is called as filter(req, res) and should return true if the kind of response should be compressed and false if it shouldn’t.

The default function uses the compressible module to check if the res.getHeader('Content-Type') is compressible.

level

The compression level to apply The higher the level of compression, the longer it’ll take to complete.

The values range from 0 (no compression) to 9 (highest level of compression). -1 can be used to mean the default compression level, which is equivalent to level 6.

memLevel

This sets how much memory should be allocated for compression and is an integer ranging from 1 for the minimum level to 9 for the maximum.

Default is zlib.Z_DEFAULT_MEMLEVEL or 8.

strategy

This sets the compression algorithm. The value only differs in the compression ratio and doesn’t affect the content of the compressed output.

The following are available:

  • zlib.Z_DEFAULT_STRATEGY — used for normal data
  • zlib.Z_FILTERED — used for data produced by a filter. Filtered data consists mostly of small values with a random distribution. This algorithm is tuned to compressed these kinds of data better. This algorithm forces more Huffman coding.
  • zlib.Z_FIXED — use to prevent the use of dynamic Huffman codes
  • zlib.Z_HUFFMAN_ONLY — force Huffman encoding only
  • zlib.Z_RULE — limit match distances to one, This gives a compression ratio as good as Huffman only, but gives better compression for PNG data

threshold

The byte threshold for response body size before compression is considered for the response. The default 1kb . The value can be the number of bytes or any string applied by the bytes module.

windowBits

The default value is zlib.Z_DEFAULT_WINDOWBITS or 15.

.filter

The default filter function, which we can use to construct a custom filter function that extends the default action.

res.flush

A res.flush method is added to force partially compressed response to be flushed to the client.

Example

For example, we can use it as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const compression = require('compression');
const app = express();  
const shouldCompress = (req, res) => {  
  if (req.headers['x-no-compression']) {  
    return false  
  }  
  return compression.filter(req, res);  
}

app.use(bodyParser.json());  
app.use(compression({  
  filter: shouldCompress,  
  level: 7,  
}));

app.get('/', (req, res) => {  
  const foo = 'foo';  
  res.send(foo.repeat(3000));  
});

app.listen(3000);

In the code above, we add the compression middleware and specify some options.

We specified that we compress our responses when the x-no-compression header isn’t present.

Also, we changed the compression level.

With the x-no-compression header present, our response is 9KB. After compression is enabled by removing the x-no-compression header, the response is only 402 bytes.

As we can see, the difference is huge. This is because the text is repeated so it can just keep one part of it and then repeat it instead of storing the whole string.

Server-Sent Events

We can compress responses with server-sent events. To do this, we compress the content after a window of the output has been buffered.

Once we have that, then we can send the data to the client.

We can use the res.flush() method to send whatever is present to the client.

For example, we can write:

const express = require('express');  
const bodyParser = require('body-parser');  
const compression = require('compression');
const app = express();  
const shouldCompress = (req, res) => {  
  if (req.headers['x-no-compression']) {  
    return false  
  }  
  return compression.filter(req, res);  
}

app.use(bodyParser.json());  
app.use(compression({  
  filter: shouldCompress,  
  level: 7,  
}));

app.get('/', (req, res) => {  
  res.setHeader('Content-Type', 'text/event-stream');  
  res.setHeader('Cache-Control', 'no-cache');  
  const timer = setInterval(() => {  
    res.write('foo');  
    res.flush()  
  }, 2000) res.on('close', () => {  
    clearInterval(timer)  
  })  
});

app.listen(3000);

To send a text event stream to the client, then we should see foo on the screen added every 2 seconds.

Conclusion

The compression middleware is useful for compressing regular responses and server-sent event output.

We can set options like compression level, chunk size, etc.

Also, if we want to compress server-side events, we should call res.flush to send what’s what already buffered to the client compressed.

Categories
JavaScript React

Creating Accessible React Apps — Focus and Semantics

React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.

In this article, we’ll look at how to create accessible React apps.

Why Accessibility?

We should create accessible apps so that they can be used by everyone.

Standards and Guidelines

The Web Content Accessibility Guidelines provide guidelines for creating accessible web sites.

Also, we have the Web Accessibility Initiative — Accessible Rich Internet Applications document contains guidelines for building fully accessible JavaScript widgets.

aria-* HTML attributes are fully supported in JSX. These attributes are kept as kebab-case as they’re in HTML.

Semantic HTML

HTML tags are named for their meanings. This means that they make sense for screen readers and other accessibility programs that parse web content.

This means that instead of using div to group elements, we should use Fragments instead so that our app doesn’t have extra div elements getting in the way of screen readers.

For example, we can add fragments to a component as follows:

function ListItem({ item }) {  
  return (  
    <Fragment>  
      <dt>{item.term}</dt>  
      <dd>{item.meaning}</dd>  
    </Fragment>  
  );  
}

class App extends React.Component {  
  constructor(props) {  
    super(props);  
    this.state = {  
      items: [  
        { term: "coffee", meaning: "black drink" },  
        { term: "milk", meaning: "white drink" }  
      ]  
    };  
  } 

  render() {  
    return (  
      <dl>  
        {this.state.items.map(item => (  
          <ListItem item={item} key={item.id} />  
        ))}  
      </dl>  
    );  
  }  
}

Then we get the following HTML rendered:

<dl>  
  <dt>coffee</dt>  
  <dd>black drink</dd>  
  <dt>milk</dt>  
  <dd>white drink</dd>  
</dl>

As we can see, fragments do not produce any extra HTML but let us group elements together.

A shorthand or fragments are <> and </> . Therefore, we can rewrite the code above as follows:

function ListItem({ item }) {  
  return (  
    <>  
      <dt>{item.term}</dt>  
      <dd>{item.meaning}</dd>  
    </>  
  );  
}

We keep the rest of the code the same.

Then we get the same HTML rendered.

Accessible Forms

We should label forms to provide accessibility for all users.

To do this, we add a for attribute to label elements. In React, the for element represented by the htmlFor prop.

This applies to all form controls.

For example, we can do the following to add a htmlFor prop:

class App extends React.Component {  
  constructor(props) {  
    super(props);  
    this.state = { name: "" };  
  } 

  render() {  
    return (  
      <>  
        <label htmlFor="name">Name:</label>  
        <input type="text" name="name" value={this.state.name} />  
      </>  
    );  
  }  
}

In the code above, we have:

htmlFor="name"

to relate the label to the form field. The htmlFor value should be the same as the name value of the form control element.

The full accessibility guidelines are at:

Notifying the user of errors

Errors need to understood by users. The following guides tells us how to expose error text to screen readers:

Focus Control

We should only change or remove outlines by using CSS so that users can get the focused element even without seeing the outline.

Ways to Skip to Desired Content

Also, we should provide ways for users to skip to what they want to see or use.

We can implement that with internal page anchors and some styling.

We can also put important content in elements like main and aside so that they can be differentiated from other content.

Programmatically Managing Focus

In React, we can access DOM elements by using refs. This means that we can control when an element is in focus by calling DOM methods to focus an element.

For example, if we want to focus an element when the component is mounted in the DOM tree, we can call focus as follows:

class App extends React.Component {  
  constructor(props) {  
    super(props);  
    this.textInput = React.createRef();  
  } 

  componentDidMount() {  
    this.textInput.current.focus();  
  } 

  render() {  
    return <input type="text" ref={this.textInput} />;  
  }  
}

In the code above, we created a ref to access the DOM element by writing:

this.textInput = React.createRef();

In the input element, we have:

ref={this.textInput}

to associate the input element with the ref we created.

Then in the componentDidMount hook, we have:

this.textInput.current.focus();

to focus the input when the component mounts. this.textInput.current returns the input element, so that we can call the native focus method on it.

Conclusion

When creating React apps, we should have accessibility in mind.

This means that we should use semantic HTML tags so that all users can understand what a page has.

Also, we should provide ways for users to skip to the content that they want to see.

Finally, we can focus elements programmatically so that users can use inputs right away when focus is lost by regaining focus.