[ad_1]

One of the nice things about front-end web development as a career choice is that the software and coding languages are available on every modern machine. It doesn’t matter what operating system or how powerful your machine is. HTML, CSS, and JavaScript will run *pretty well* on it. This lets a lot of us in the industry bypass the need for formal education in computer science.

Unfortunately, this also has the side effect of leaving little gaps in our knowledge here and there, especially in strategies like bitmasking, which are seldom used in web development.

## What is a bitmask?

Bitmasking is a strategy that can be used to store multiple true-or-false values together as a single variable. Depending on the language, this can be more memory efficient and also opens up the doors to some special operators that we’ll look at later. This trick takes advantage of two simple facts:

- humans and computers look at numbers differently.
- the way computers think about numbers is identical to how they think about true and false.

Humans typically think of numbers in a decimal, or base-10, system. We have 10 unique digits of 0-9, and when we want to count beyond 9, we create new columns as needed to symbolize how many multiples of ten, one hundred, one thousand, (the powers of ten), and so on we need. Computers, on the other hand, look at numbers in a binary or base-2 system. Computers have 2 unique digits, 0 or 1, and when they need to count beyond that, they create new columns to symbolize how many multiples of 2, 4, or 8 (the powers of two) *they* need. While we think of numbers differently, the values of integers are ultimately identical, and the computer stores all numbers as binary values. Each individual binary digit is a *bit *of information.

English | Base-10 | Base-2 |
---|---|---|

Zero | 0 | 0 |

One | 1 | 1 |

Two | 2 | 10 |

Three | 3 | 11 |

Four | 4 | 100 |

Five | 5 | 101 |

Six | 6 | 110 |

Seven | 7 | 111 |

Eight | 8 | 1000 |

Nine | 9 | 1001 |

Ten | 10 | 1010 |

When we combine the fact that all our numbers are converted to binary for storage with the fact that boolean true/false values are also stored as a 1 or 0, respectively, we can see how we could easily store a group of boolean values as a single integer. All we have to do is make sure each value we care about is stored as a separate power of two.

## Where might I see this?

A great example of this in front-end development is the Node.compareDocumentPosition method. This method compares the relative positioning between two nodes on a page and returns a bitmask of the resulting comparison. There are six possible values of `a.compareDocumentPosition(b)`

- Disconnected – These nodes are not in the same document tree (for example, one node is in a web component’s shadow DOM)
- Preceding – Node a follows node b in the document tree.
- Following – Node b follows node a in the document tree.
- Contains – Node a is a descendant of node b.
- Contained By – Node a is an ancestor of node b.
- Implementation Specific – This rarely means anything to us and is an artifact of how the calculation is done within the browser.

The result of our comparison could yield any combination of those 6 being true or false, a potential 64 unique combinations! The way we make sense of this, however, is to assign each value a bit. Since we have 6 values, we’ll need six bits.

Disconnected | 1 | 000001 |

Preceding | 2 | 000010 |

Following | 4 | 000100 |

Contains | 8 | 001000 |

Contained By | 16 | 010000 |

Implementation Specific | 32 | 100000 |

Now our 64 possible combinations can be numbered from 0 (all are false) to 63 (all are true). Of course, not *all* combinations are actually possible, as nodes can neither precede *and* follow nor contain *and* be contained by. Nevertheless, when we examine the returned number bit-by-bit, we can tell exactly which values are true and which are false.

## How do I use this?

One great use of this is in the focus-trapping logic in IBM’s Carbon Design System. Since we want to prevent focus from leaving the modal and instead loop it back into the element, we have a `focusout`

event listener on the modal’s container element. When focus leaves an element within the modal, the `focusout`

event bubbles up, and we’re able to see the event’s target element that just lost focus, as well as the event’s `relatedTarget`

element that just gained focus. We can then compare the positioning of the `relatedTarget`

to the modal container, and if the “contains” value is not true, we know we need to force focus back into the modal.

While we could split out the bits and do individual comparisons, JavaScript has *bitwise operators* designed specifically to compare two bitmasks and yield a third bitmask. These operators will compare each individual bit and then yield a 0 or a 1.

- & will evaluate to 1 when two compared bits are
*both*1 - | will evaluate to 1 when either compared bit is 1
- ^ will evaluate to 1 when one, but not both, compared bits are 1.

Try comparing 5 and 9 with each operator in this truth table to see bitwise calculations in action:

https://codepen.io/andy-blum/pen/ExEajpX

Once we understand these comparison operations, we can use them within our code. We’ll start by creating the combination flags `PRECEDING`

and `FOLLOWING`

. These new flags combine the bitmasks provided by the `Node`

object. In our use case, `PRECEDING`

will indicate that the compared node’s tab order should be prior to the current node and `FOLLOWING`

will indicate the opposite. We’ll also create a bitmask `WITHIN`

that will be easier to read in our code later.

```
const PRECEDING = Node.DOCUMENT_POSITION_PRECEDING | Node.DOCUMENT_POSITION_CONTAINS;
const FOLLOWING = Node.DOCUMENT_POSITION_FOLLOWING | Node.DOCUMENT_POSITION_CONTAINED_BY;
const WITHIN = Node.DOCUMENT_POSITION_CONTAINED_BY;
```

Next, within our `focusout`

event listener, we can compare the relative positions of the event’s `target`

, which just lost focus and its `relatedTarget`

, which just gained focus. The code below has been modified slightly from the source to make it easier to read and has comments pointing to the breakdown below.

```
function handleFocusOut(event) {
const { target, relatedTarget } = event;
// #1
const positionToModal =
this.compareDocumentPosition(relatedTarget) |
(this.shadowRoot?.compareDocumentPosition(relatedTarget) || 0);
// #2
const positionToPrevious = target.compareDocumentPosition(relatedTarget);
// #3
const relatedTargetIsContained = Boolean(positionToModal & WITHIN);
// #4
if (!relatedTargetIsContained && !(relatedTarget === this)) {
// #5a
if (positionToPrevious & PRECEDING)
// #6a
tryFocusElems(focusableElements as [HTMLElement], true, this);
// #5b
} else if (positionToPrevious & FOLLOWING) {
// #6b
tryFocusElems(focusableElements as [HTMLElement], false, this);
}
}
};
```

Let’s break it down:

- We create a bitmask variable,
`positionToModal`

. This is a combination of the comparison between the modal and the`relatedTarget`

as well as the modal’s shadowroot and the`relatedTarget`

. The element we’ve focused to could be in either the regular document or in the Shadow DOM, so we want to compile both comparisons together. - We create a bitmask variable,
`positionToPrevious`

. This is the comparison of the target and the related target. - We create a boolean variable,
`relatedTargetIsContained`

. This compares`positionToModal`

and`WITHIN`

. If the element we focused on is in any way*inside our modal*, then this variable is true. - We check to see if our
`relatedTarget`

is contained within the modal and that our`relatedTarget`

is not the modal itself. If that’s true, then we know our`relatedTarget`

is*outside*the modal, and we need to redirect focus. - We compare our
`positionToPrevious`

bitmask with our`PRECEDING`

and`FOLLOWING`

bitmasks. If they overlap, then we know which way to try to focus, and we use our`tryFocusElems`

function to move focus back into the modal. - The
`tryFocusElems`

function systematically attempts to place focus on each element in`elems`

. It can run in forward or reverse order based on the second argument, and if none of the elements provided will hold focus, it falls back to the element provided in the third argument

## Conclusion

Bitmasks and bitwise operations are not strategies front-end developers are likely to reach for often, but having a solid foundation of computer science principles can help us to know when they are the right tool to use. Understanding the theory behind how numbering systems work and how computers can compare and manipulate masks can open up new opportunities in our code.

[ad_2]

Source link