This guest blog post was written by Rona Kwestel, the head of technical communication at Axoni. She transitioned from software development to technical writing almost 20 years ago and hasn’t looked back since. Flare has been her tool of choice since first testing it out in the late aughts, and she especially likes to play with the interactive features that enhance the user experience.

Challenge

When building documentation with the side navigation skin in Flare, the left navigation pane width can be set to a fixed size or a fixed percentage of the page, but once built, the viewer is stuck with whatever you picked. In our case, it was set to a fixed percentage, but depending on the width of the screen, this was often more space than necessary and I could feel my fingers itching to resize it manually to maximize the content space. Surely there was a way…

notes icon

A little note about how I solve these kinds of web design issues; in a word, I google. The internet is flush with videos, articles, Q&As, and reference pages on web development. While it’s rare to find an exact solution to my specific problem, much like seeking a recipe for a dish I have in mind, I take a little nugget from each related solution I find and cobble them together to get what I want. A key piece in solving the puzzle comes from using Chrome DevTools; being able to see what’s going on and try things out on the fly is critical to making the page look and work the way I intend.

Solution

One option I considered was to simply add the resize: horizontal property to the nav pane div element. This did exactly what I wanted, but unfortunately does not provide any properties to change the appearance or location of the resizer, which appears as a triangular set of lines at the bottom right corner of the div and feels unintuitive and inconvenient.

Instead, I used JavaScript to add a narrow div between the nav pane and the main content area on page load. This extra div provided the vertical divider on which I could specify a resize cursor along with event listeners to capture the mouse movements for determining the new size of the nav pane to its left. The layout is controlled using flexbox, and the new width is stored in the browser's web storage for retrieval when another page is loaded so that the desired size is retained for the entire site.

The implementation involved updates to the following components in the Flare project:

JavaScript

The JavaScript file below was added to the Resources/Scripts folder to do the following:

  • Add a nav border div on page load.
  • Add event listeners to the nav border div for mouse down/move/up events.
  • Resize the nav bar to the newly determined width based on the mouse events.
  • Store the new width value in localStorage and retrieve it on page load so that all pages within the site retain the same nav pane width.

nav-resize.js

/* Add a resize bar to the navigation pane */

// Define the key with which the current width will be stored and retrieved
// from localStorage so that the width remains constant when changing pages
// within the site.
const lsKey = "navwidth";

// Utility function to set the nav pane width to the specified width
function setNavPaneWidth(width) {
    const navPane = document.getElementsByClassName('sidenav-wrapper')[0];
    navPane.style.width = width;
}

// If the nav pane width has been previously set, keep it at the existing
// width stored in localStorage when loading a new page within the site.
function retainNavPaneWidth() {
    const navWidth = localStorage.getItem(lsKey);

    if (navWidth != "") {
        setNavPaneWidth(navWidth);
    }
}

// Resize the nav pane according to the current x-coordinate of the mouse
// position and store that width in localStorage for retrieval on a new page
// within the site.
function resize(e) {
    const width = e.clientX + "px";

    setNavPaneWidth(width)
    localStorage.setItem(lsKey, width);
}

// Add a div between the nav pane and the main content window to which an
// ew-resize cursor and the resizing mouse event behavior can be attached
// to allow the nav pane width to be manually resized.
function addNavBorder() 
{
    // Create the navBorder div and class it with nav-border for CSS styling
    const navBorder = document.createElement('div');
    navBorder.classList.add('nav-border');

    // Get the sidenav-layout div and append the navBorder div as a child;
    // the flexbox order settings on the divs in the CSS will make sure
    // it ends up between the nav pane and the main content div
    const contentRow = document.getElementsByClassName('sidenav-layout')[0];
    contentRow.appendChild(navBorder);

    // Add an event listener to the nav border on mousedown, whose anonymous
    // function in turn adds event listeners to the document on:
    // - mousemove, whose named function resizes the nav pane
    // - mouseup, whose anonymous function removes the mousemove event listener
    navBorder.addEventListener("mousedown", function () {
        document.addEventListener("mousemove", resize);
        document.addEventListener("mouseup", function () {
            document.removeEventListener("mousemove", resize);
        });
    });
}

// When the page is ready, check for the current setting of the nav pane width
// and add the nav border for resizing
$(document).ready(function () {
    retainNavPaneWidth();
    addNavBorder();
});

CSS

The CSS code below was added to the stylesheet under Resources/Stylesheets to do the following:

  • Use the flexbox order property to ensure that the nav border div appears between the nav pane and main content area (this overrides the default order of appearance, where the nav border was appended after its siblings via the JavaScript above).
  • Style the nav border div to function as a resizer for the nav pane.
  • Ensure the nav border div is not displayed when the nav pane itself is not displayed due to the screen being in tablet size or smaller.

Styles.css

.sidenav-wrapper 
{
    /* ensure sidenav-wrapper comes before nav-border in sidenav-layout
flex row */
    order: 1;

    /* avoid horizontal scrolling on overflow */
    overflow-x: hidden;
}

.nav-border {
    /* resizable nav-border between nav menu and body content added via
nav-resize.js script */

    background-color: #e1e1e175;

    /* ensure nav-border comes between sidenav-wrapper and body-container in
sidenav-layout flex row */
    order: 2;

    /* allow horizontal (east-west) resizing only */
    cursor: ew-resize;

    /* don't allow nav-border to grow or shrink, and make it 3px wide */
    flex: 0 0 3px;

    /* ensure that text isn't selected while the mouse is being dragged */
    user-select: none;
}

.body-container {
    /* ensure body-container comes after nav-border in sidenav-layout
flex row */
    order: 3;
}

@media only screen and (max-width: 1279px) 
{
    /* hide nav border when nav pane is not displayed */
    .nav-border {
        display: none;
    }
}

Master Page

Finally, the HTML code below was added at the bottom of the <body> tag in the SideNavigation.flmsp master page under Resources/MasterPages to load the nav-resize.js script on page load.

SideNavigation.flmsp

<!-- Adds a resize bar to the navigation pane -->
<script type="text/javascript" src="../Scripts/nav-resize.js">
</script>

Demo

The result is shown in the short video clip below.