f w h

ProgressBar.js for React with ES6

If you’re not aware, ProgressBar.js is a great way to create beautiful, animated progress bars with JavaScript. The library outputs and automatically animates your progress bar with SVG.

There are a couple of React implementations out there, including one by the creator of the original library. However, they are rather limited in the options they let you set. So, I created react-es6-progressbar.js. The package exposes a set of React components to which you can pass the library’s full set of options.

Get the library here:

NPMnpm install –save react-es6-progressbar.js

GitHub

Populating Multiple Fields and Levels with Mongoose

Mongoose, the popular MongoDB library for NodeJS is incredibly robust and relatively easy to pick up. Its documentation, however leaves a little to be desired.

To attain functionality similar to join functionality present in relational databases, Mongoose provides a method called populate(). Used in conjunction with the ref schema property, data can be pulled in from other documents in place of an id. The process is fairly straightforward. However, what Automattic’s documentation doesn’t explain is how to populate multiple paths, and the documentation for multiple levels leaves much to be desired.

The documentation does point out that chaining multiple populate functions won’t work. How, then can this be done? Multiple paths may be populated by creating a space-separated list. Note the emphasis, because comma-separated lists do not work. By way of example:


// Doesn't work - will only populate 'friends'
Users.findOne({/* query here */})
.populate('address')
.populate('friends');

// Works
Users.findOne({/* query here */})
.populate('address friends');

Now, what if we wanted to populate the friends’ addresses? As explained in the docs, you would nest another populate by passing an object to the method, and adding another populate as a property. That sounds incredibly confusing, I’m sure, but here’s what I mean.


// This time, we're not passing a string, but an object to populate()
Users.findOne({/* query here */})
.populate({
  path: 'address friends', // The string we passed in before
  populate: {
    path: 'address' // This will populate the friends' addresses
  }
});

Stop Setting Breakpoints

Ever since Ethan Marcotte’s amazing article on ALA way back in 2010, web developers have been leveraging media queries to ensure frontend designs are usable across devices. In general, that means setting breakpoints with new styles for each device size. This new idea lurched web interface design forward, and the idea of responsive web design has become not only ubitquitous, but standard. Most web design/development courses include the concept as part of their curriculum.

What web developer hasn’t played with the width of their browser window, resizing until it breaks, and then setting a breakpoint at that width. Lather, rinse, repeat until the design works cross-browser. I am a firm believer in the concept that if it works, it’s not wrong. However, I think there’s a better way.

Viewport Units

Support for viewport units in CSS is rather high. The vh and vw units are almost universally usable. I would say for almost all projects, they’re safe to use. Support for vmax and vmin isn’t quite as good, and that’s the only reason I even discuss continued use of media queries at all in this article. Put another way, once vmin and vmax support widens, media queries can become almost unnecessary.

The Only Media Query You’ll Ever Need

I present to you the only media query you’ll ever need: orientation. The team up of viewport units and the orientation query can’t solve every design problem, but it can come pretty close.

Consider this scenario: You are working on a design in-browser. It has three blocks arranged horizontally. In landscape, which is the orientation most desktop users are going to be using, The blocks fit well, and their content is easily readable. However, below 600px width, the content overflows the box, causing the last box to drop to the next line. Normally in a situation like this, because the font-size is set using em, rem or some other either relative or static sizing, you would likely set a breakpoint at 600px width, then adjust the font size.

See the Pen wpjdGN by Ronald Roe (@ronaldroe) on CodePen.

This is where viewport units come in. Instead of using the relative or static units like normal, set the font-size based on the width of the viewport. For most paragraph text, I find 2 – 3vw works well, but use whatever works for your design.

With the vw units set, the content will no longer overflow the box as the size shrinks. In most cases, the breakpoint becomes unnecessary. Next, in our imaginary scenario, we try the design in portrait orientation on a phone and find that the boxes are small and the text is too small to read. What happened? Since the text size is based on the viewport’s width, and the width is much smaller than the height, the text looks tiny.

That’s where our orientation media query comes in. Setting @media (orientation: portrait) will trigger when the height of the viewport is greater than the width. Now, inside the query, reset the font size to use vh units. In general, using the same number as the vw value will maintain a similar if not consistent look. Additionally, most scenarios with floated boxes will look better if the float is removed (set to none) in portrait.

See the Pen baMWRP by Ronald Roe (@ronaldroe) on CodePen.

There will be cases where breakpoints are still necessary. However, this method should reduce the need considerably. I’ve used this method in all of my current and some of my recent past projects, and am having a lot of success with it. For my current project, a frontend for Shape Fitness, You will not find a single breakpoint in the CSS.

Let me know in the comments and on Twitter what you think.

Viewport Units on SVG

A quick note, learned from experience: viewport units (vw, vh, vmin, vmax) do not work as height or width attributes in Firefox. I tested the latest (as of this post’s pub date) versions of: Chrome, Firefox, Edge and IE11. It seems to work perfectly in all of those except Firefox.

What do I mean? Take a look:


<svg height="10vh" width="10vw">
	<!-- SVG innards -->
</svg>

This works with no issue in most browsers, but will be ignored in Firefox. Why? I’m guessing it’s Firefox sticking closely to the spec, as Mozilla doesn’t list them as an available option in their MDN document on SVG sizes, and neither does the W3C spec for the length data-type on SVGs.

Fret not, however, because I have a fix for it.

Viewport units are simply a percentage of the viewport’s, or as the CSS spec states, ‘initial containing block’ size. Thus, if your viewport is 1000px wide, 1vw is equivalent to 10px. The only problem is that it will need to adjust automatically when the viewport changes.

The script below sets the viewport units aside in a data-* attribute, then uses them to calculate a pixel value for them:


// My viewport script from a previous post
function viewport(){

	let w = window.innerWidth;
	let h = window.innerHeight;
	
	let orientation = w > h ? 'landscape' : 'portrait';
	
	let output = {
		w: w,
		h: h,
		orientation: orientation
	};

	return output;

}

// Converts the viewport units to a pixel-based length
function viewport2px(size){

	let int = parseInt(size, 10); // Grabs the size and extracts only the number
	let output;
	
	size.includes('h') ? output = ((int / 100) * viewport().h) + 'px' :  output = ((int / 100) * viewport().w) + 'px';
	
	return output;

}

// Sets the new units according to their current size
function sizeSVGs(svgsIn){

	// If a NodeList object is not provided, just select all of them
	let svgs = svgsIn || document.querySelectorAll('svg');
	
	Array.from(svgs).forEach(function(svg){
		// Check the height for viewport units (doesn't check for vmin/vmax - easy enough to add),
		// Sets those units aside for later use,
		// Sets the new px size
		if(svg.getAttribute('height').includes('vw') || svg.getAttribute('height').includes('vh')){
			svg.setAttribute('data-height', svg.getAttribute('height'));
			svg.setAttribute('height', viewport2px(svg.getAttribute('height')));
		}
		// Does the same thing for width
		if(svg.getAttribute('width').includes('vw') || svg.getAttribute('width').includes('vh')){
			svg.setAttribute('data-width', svg.getAttribute('width'));
			svg.setAttribute('width', viewport2px(svg.getAttribute('width')));
		}
	});

}

// Fire on load
window.addEventListener('load', function(){

	sizeSVGs();

});

But what about the resize? Well, remember how we set aside the initial values in a data-*attribute? We just need to grab that value again and recalculate any time a resize event fires.


window.addEventListener('resize', function(){
	Array.from(svgs).forEach(function(svg){
		if(svg.hasAttribute('data-height')) svg.setAttribute('height', vp2px(svg.getAttribute('data-height')));
		if(svg.getAttribute('data-width')) svg.setAttribute('width', vp2px(svg.getAttribute('data-width')));
	});
});

Caveat: This script uses the Array.from()method to convert the SVG NodeList into an array for iteration. No version of IE supports this method. If IE support is required, MDN has a great drop-in polyfill.

WordPress Development Environment Scripts

It is useful to have certain items or scripts available to you while developing that you may not want present in production. Forgetting to remove these items before you go to production can be dangerous.

When developing with WordPress, a short script in your functions.php can add these tools when you’re developing locally, but hide them when live.

Create your scripts in a separate file in the js directory of your theme. I called mine dev.js. Then, add the following to your functions.php file:


function add_dev_class( $classes ) {
 
    $classes[] = 'dev';
     
    return $classes;
     
}

function enqueue_dev(){

    wp_register_script( 'Dev', get_stylesheet_directory_uri() . '/js/dev.js', [ 'jquery' ], NULL, true );
    wp_enqueue_script( 'Dev' );

}

// Here comes the magic
if( $_SERVER[ 'SERVER_NAME' ] == "localhost" ){

    add_filter( 'body_class', 'add_dev_class' );
    add_action( 'wp_enqueue_scripts', 'enqueue_dev' );

}

The first function will add a class called “dev” to the body tag, so you can hook it with JavaScript or add development-centric CSS styles.

The second function will enqueque the dev.js script file.

Finally, the conditional at the end will check what the current base address is and if it matches, will execute the functions. I’ve used localhost here, but you can use whatever your development domain is.

I use this to load a variation of my viewport size bookmarklet that sits at the bottom of the window with a plethora of information to help inform my decisions when designing for responsiveness.

Next Page »