By Ben Nadel

Zero and zero

www.bennadel.com/blog/3941-s…

If you read the documentation on Console on MDN, you’ll know that Console objects have a lot of interesting features, like String Substitution and CSS Styling. These functions work if you take a special token as the first argument to the console.log() method, and then decorate the first argument with a series of arguments that follow.

For example, we can use %o to insert an object:

console.log( "My document: %o".document )
Copy the code

You can also use %c to change the style of the same log:

console.log( "%cMy document: %o"."color: red ;".document )
Copy the code

Note: The second and third arguments are used to modify %c and %o, respectively. In this example, color: red; Affects everything after %c.

With that said, let’s take an interesting example of what it can do:

<! doctypehtml>
<html lang="en">
<head>
	<meta charset="utf-8" />
	<title>
		Styling console.log() Output Formatting With CSS
	</title>
</head>
<body>

	<h1>
		Styling console.log() Output Formatting With CSS
	</h1>

	<script type="text/javascript">

		console.log(
			"%cAltering the text color"."color: fuchsia"
		);

		console.log(
			"%cAltering the text experience"."background-color: fuchsia ; color: white ; font-weight: bold ; " +
			"font-size: 20px ; font-style: italic ; text-decoration: underline ; " +
			"font-family: 'american typewriter' ; text-shadow: 1px 1px 3px black ;"
		);

		// NOTE: In this demo, inline-block isn't needed in Chrome; but, it is needed in
		// Firefox or the block properties, like padding, don't work. Trying to use
		// "block" will work in Chrome; but, will go full-width in Firefox.
		console.log(
			"%cAltering the box display"."display: inline-block ; border: 3px solid red ; border-radius: 7px ; " +
			"padding: 10px ; margin: 20px ;"
		);

		// NOTE: Background-images work in Chrome, but not in Firefox. Also, at least
		// locally, only fully qualified URLs seems to work (but that may have been
		// something I was messing up).
		// --
		// Also, it doesn't look like width/height work on box-model. As such, I am using
		// padding to push-out the box size.
		console.log(
			"%cBackground image"."display: inline-block ; background-image: url( 'https://bennadel.github.io/JavaScript-Demos/demos/console-log-css/rock.png' ) ; " +
			"background-size: cover ; padding: 10px 175px 158px 10px ; " +
			"border: 2px solid black ; font-size: 11px ; line-height: 11px ; " +
			"font-family: monospace ;"
		);

		// The same CSS styling can be used with many of the other console methods, like
		// the .group() method.
		console.group(
			"%cGrouped Output"."background-color: #e0005a ; color: #ffffff ; font-weight: bold ; padding: 4px ;"
		);
		console.log( "Groups are cool for grouped stuff." );
		console.log( "Totes magotes" );
		console.groupEnd();

	</script>

</body>
</html>
Copy the code

We tried putting a lot of CSS styles in console.log(), and when we run the above code in Chrome, we see the following output in the developer tools console:

Firefox also supports these, but it doesn’t seem to support background images.

Isn’t he very handsome? The problem is that the code in console.log() is too redundant. How can we better encapsulate these features – hide complex CSS styles and still provide the flexibility of console.log()? Fortunately, JavaScript is a powerful language that isn’t too complicated to encapsulate.

To implement this idea, I created echo objects to replace console objects. That said, there are some console-like methods:

  • echo.log()
  • echo.error()
  • echo.warn()
  • echo.trace()
  • echo.group()
  • echo.groupEnd()

Just like the console methods, these echo methods will be mutable — they accept a dynamic number of arguments. One big difference here is that the Echo object will provide formatting methods that can be used to format an input:

  • echo.asAlert()
  • echo.asWarning()
echo.log( echo.asAlert( "Oh no!" ), "Something went wrong!")
Copy the code

The code above will format the Oh NO part so that it has an “alert style” and “Something wen wrong!” This input remains plain text.

Let’s see how this works:

<! doctypehtml>
<html lang="en">
<head>
	<meta charset="utf-8" />
	<title>
		Styling console.log() Output Formatting With CSS
	</title>
</head>
<body>

	<h1>
		Styling console.log() Output Formatting With CSS
	</h1>

	<script type="text/javascript">

		// I provide a logging API that has some special sauce for formatting.
		var echo = (function() {

			var queue = [];
			var ECHO_TOKEN = {};
			var RESET_INPUT = "%c ";
			var RESET_CSS = "";

			// Attach formatting utility method.
			function alertFormatting( value ) {

				queue.push({
					value: value,
					css: "display: inline-block ; background-color: #e0005a ; color: #ffffff ; font-weight: bold ; padding: 3px 7px 3px 7px ; border-radius: 3px 3px 3px 3px ;"
				});

				return( ECHO_TOKEN );

			}

			// Attach formatting utility method.
			function warningFormatting( value ) {

				queue.push({
					value: value,
					css: "display: inline-block ; background-color: gold ; color: black ; font-weight: bold ; padding: 3px 7px 3px 7px ; border-radius: 3px 3px 3px 3px ;"
				});

				return( ECHO_TOKEN );

			}

			// I provide an echo-based proxy to the given Console Function. This uses an
			// internal queue to aggregate values before calling the given Console
			// Function with the desired formatting.
			function using( consoleFunction ) {

				function consoleFunctionProxy() {

					// As we loop over the arguments, we're going to aggregate a set of
					// inputs and modifiers. The Inputs will ultimately be collapsed down
					// into a single string that acts as the first console.log parameter
					// while the modifiers are then SPREAD into console.log as 2... N.
					// --
					// NOTE: After each input/modifier pair, I'm adding a RESET pairing.
					// This implicitly resets the CSS after every formatted pairing.
					var inputs = [];
					var modifiers = [];

					for ( var i = 0 ; i < arguments.length ; i++ ) {

						// When the formatting utility methods are called, they return
						// a special token. This indicates that we should pull the
						// corresponding value out of the QUEUE instead of trying to
						// output the given argument directly.
						if ( arguments[ i ] === ECHO_TOKEN ) {

							var item = queue.shift();

							inputs.push( ( "%c" + item.value ), RESET_INPUT );
							modifiers.push( item.css, RESET_CSS );

						// For every other argument type, output the value directly.
						} else {

							var arg = arguments[ i ];

							if((typeof( arg ) === "object") | | (typeof( arg ) === "function" )
								) {

								inputs.push( "%o", RESET_INPUT );
								modifiers.push( arg, RESET_CSS );

							} else {

								inputs.push( ( "%c" + arg ), RESET_INPUT );
								modifiers.push( RESET_CSS, RESET_CSS );

							}

						}

					}

					consoleFunction( inputs.join( "" ), ...modifiers );

					// Once we output the aggregated value, reset the queue. This should have
					// already been emptied by the .shift() calls; but the explicit reset
					// here acts as both a marker of intention as well as a fail-safe.
					queue = [];

				}

				return( consoleFunctionProxy );

			}

			return({
				// Console(ish) functions.
				log: using( console.log ),
				warn: using( console.warn ),
				error: using( console.error ),
				trace: using( console.trace ),
				group: using( console.group ),
				groupEnd: using( console.groupEnd ),

				// Formatting functions.
				asAlert: alertFormatting,
				asWarning: warningFormatting }); }) ();// --------------------------------------------------------------------------- //
		// --------------------------------------------------------------------------- //

		// Let's try mixing a bunch of values together.
		echo.log(
			echo.asAlert( "This is great!" ),
			"Right!",
			{ "i am": "an object" },
			null["an array"].function amAFunction() {},
			echo.asWarning( "I mean, right? ! ? ! ? !")); echo.log();// Let's try a more sensible example.
		echo.group( echo.asWarning( "Arnold Schwarzenegger Movies")); echo.log("The Running Man" );
		echo.log( "Terminator 2", echo.asAlert( "Amazing!" ), echo.asWarning( "TOP 100")); echo.log("Predator" );
		echo.log( "Twins", echo.asWarning( "TOP 50")); echo.groupEnd(); echo.log(); echo.log( echo.asAlert("Winner Winner" ), "Chicken dinner!" );

	</script>

</body>
</html>
Copy the code

The above code may be a little difficult to understand immediately, but formatting methods such as asAlert() and asWarning() are key. These methods push values and CSS styles into the internal queue. These console’s proxy methods then merge the internal queues into a single console call.

If we run the demo in Chrome, we can see the following console output:

The abstraction of the Echo object makes it surprisingly easy for an input to be styled or not to be styled in the console output. We don’t have to worry about which parameter corresponds to which token, you just need to add the input to the corresponding formatting method.

(after)

Your “like” will give me a good mood, and it will be even more perfect if I can get a star.