Tag Archives: Recursion

Embedding Stylesheets in JavaScript

Now Available in YourJS

One of the cool things about JS is that you can do almost anything with it! The only thing is that you need to have the tools to get that “anything” job done. Recently, I had the desire to embed HTML, CSS, and JS all in one file that would be loaded when a specific browser event fired. The common solution is to use a library such as jQuery to load an HTML snippet but I wanted a different solution. I was thinking it would be great to be able to do something like this:

css({
    'div.special-1, span.special-2': {
        fontFamily: 'Trebuchet MS',
        a: {
            textDecoration: null,
            '&:link, &:visited': { color: '#08F' },
            '&:hover, &:active': { color: 'red' }
        }
    }
});

Having this resulting in a stylesheet added to the HEAD with the following CSS:

div.special-1, span.special-2 {
    font-family: Trebuchet MS;
}
div.special-1 a, span.special-2 a {
    text-decoration: none;
}

div.special-1 a:link, span.special-2 a:link,
div.special-1 a:visited, span.special-2 a:visited {
    color: #08F;
}

div.special-1 a:hover, span.special-2 a:hover,
div.special-1 a:active, span.special-2 a:active {
    color: red;
}

After a bit of work to make a few Sass-like features work, I came up with the following JavaScript function:

Brief css(objStyles, opt_ancestors) Documentation

This function takes an object representing CSS rules and adds a stylesheet with those rules to the document.

Parameters

  1. objStyles
    An object representing the CSS rules to be inserted into the document.

    • Property names will be used as media queries if they start with "@media ".
    • Property names will be used as rule selectors if the value is an object.
    • If a property name is to be used as a selector, if any selectors don’t contain &, "& " will be prepended to it.
    • For all selectors, & will be replaced recursively with the selectors found in the parent.
    • CSS property names will be uncamelcased by inserting dashes before each uppercased character and lower casing all characters.
    • If a value is null or undefined, it will be turned into "none".
    • If a value is a number other than 0, "px" will be appended to it.
    • If a value is an array all of the items will be concatenated together, using "," to delimit the values.
    • If a value ends with ! it will be replaced with "!important".
  2. opt_ancestors
    Optional. This can be an element or an array of elements which will get another class added to target all rules to it and its children. This can alternatively be a CSS path (selector) specifying the root on which all CSS rules created should be based.

Returns

The stylesheet that is created and appended to the document is returned.

Future Development

I feel like this function turned out pretty well so I plan on incorporating it into YourJS (whenever I finish up with that :-D ). In the meantime, if I update the code, you will be able to see the updates here or in GitHub. One thing I would like to incorporate is the ability to have variables. Another nice-to-have thing would be the ability to supply more options such as where the stylesheet should be inserted (if it will be inserted at all), pretty-print, and helper functions (eg. darken). Feel free to leave a comment saying what other things may be nice to have. 8-)

JavaScript – Getting All Text Nodes

I have been working on something I am calling JS-Proofs (or jPaq Proofs). While working on the menu I was faced with the annoying issue of either not having whitespaces between my elements in the markup or removing them some other way. Since my favorite language is JS, I decided to write a function that would remove all of the text nodes with whitespaces for me. This evolved into a more general function which retrieves an array of all the text nodes contained by a given element:

/**
 * Gets an array of the matching text nodes contained by the specified element.
 * @param  {!Element} elem
 *     The DOM element which will be traversed.
 * @param  {function(!Node,!Element):boolean} opt_fnFilter
 *     Optional function that if a true-ish value is returned will cause the
 *     text node in question to be added to the array to be returned from
 *     getTextNodesIn().  The first argument passed will be the text node in
 *     question while the second will be the parent of the text node.
 * @return {!Array.}
 *     Array of the matching text nodes contained by the specified element.
 */
function getTextNodesIn(elem, opt_fnFilter) {
  var textNodes = [];
  if (elem) {
    for (var nodes = elem.childNodes, i = nodes.length; i--;) {
      var node = nodes[i], nodeType = node.nodeType;
      if (nodeType == 3) {
        if (!opt_fnFilter || opt_fnFilter(node, elem)) {
          textNodes.push(node);
        }
      }
      else if (nodeType == 1 || nodeType == 9 || nodeType == 11) {
        textNodes = textNodes.concat(getTextNodesIn(node, opt_fnFilter));
      }
    }
  }
  return textNodes;
}

What is kind of cool about the above function is that it not only allows you to get all of the child text nodes, but all descendant text nodes. This function also provides the capability of specifying an optional filtering function to just return text nodes that match a criteria. The function will be passed the text node in question and the parent of that text node. In order to specify that the text node should be added to the array returned from getTextNodesIn you must return a true-ish value.

The following is an example of how to use this function in order to remove all whitespace text nodes from an element:

var menu = document.getElementById('divMenu');
getTextNodesIn(menu, function(textNode, parent) {
  if (/^\s+$/.test(textNode.nodeValue)) {
    parent.removeChild(textNode);
  }
});

In the strictest sense I am actually kind of hacking the new function that I defined but it gets the job done. 8-)

JavaScript – Euclidean Algorithm

One of the things that you often have to learn early on in school after multiplication and division is how to find the GCD (Greatest Common Divisor) of two numbers. It is possible that you know the GCD as the GCF (Greatest Common Factor) or the HCF (Higest Common Factor). Do you know how to calculate this for any two positive integers? Many may simply list all of the positive divisors of both numbers and take the largest shared between the two numbers. On the other hand, at times it isn’t easy to list all of these positive divisors. Therefore you could use Euclid’s Algorithm to find the GCD of two numbers.

What is Euclid’s Algorithm

To use this algorithm, you should do the following:

  1. Find the remainder of dividing the larger number by the smaller one:
    remainder = max % min
  2. If the remainder is equal to 0, you can stop because you have identified the GCD which is the smaller number from the previous step.
  3. Start back at step one, using the smaller number as the larger number and the remainder as the smaller number:
    max = min
    min = remainder

Now let’s try to find the GCD of 462 and 910 using the algorithm outlined above:

  1. Find the remainder of dividing 910 (the larger number) by 462 (the smaller number):
    remainder = 910 % 462 =  448
  2. Since the remainder is not 0, we must now find the remainder of dividing 462 (the new larger number) by 448 (the new smaller number):
    remainder = 462 % 448 = 14
  3. Since the remainder is not 0 we must now find the remainder of dividing 448 (the new larger number) by 14 (the new smaller number):
    remainder = 448 % 14 = 0
  4. We know that 14 is the GCD because that was the last smaller number used that gave a remainder of 0.
Now that we can see how easy that is and how efficient it is, it is now time to implement it as a function on a computer.  Since JavaScript is my favorite language and it is the most widely available action language, I decided to define it in JavaScript:
function gcd(a, b) {
  return b ? gcd(b, a % b) : a;
}

The cool thing about this function definition is the fact that it is actually defined recursively. Although I didn’t have to define it that way, I felt it was the perfect opportunity to show how to think recursively. The following live example proves that this works for all integers:
GCD Implemenetation

That example shows the power of recursion, JavaScript, and old school mathematics! 8)