Refining a Text Resize script

I was interested in writing a small script that can offer buttons for increasing and decreasing text size. This is just a learning exercise (I’m a noob). I Googled scripts for this, but all of them were obtrusive. I’d like to make this unobtrusive, but also as efficient as possible. I’ve modified this script to make it unobtrusive, and it works, but I suspect this could be done a lot better.

<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>TextResize</title>
	
<style media="all">
body {font: 100% serif;}
.buttons span {display: inline-block; padding: 20px; background: #f7f7f7; cursor:pointer}
.buttons span:hover {background: orange;}
.buttons span:focus {background: green; outline: none;}
</style>
</head>
<body>

<p>This is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text</p>

<script>
var d = document;
var div = d.createElement("div");
div.setAttribute("class","buttons");
var spanBig = d.createElement("span");
spanBig.setAttribute("tabindex","0");
var bigText = d.createTextNode("Bigger");
spanBig.appendChild(bigText);
var spanSmall = d.createElement("span");
spanSmall.setAttribute("tabindex","0");
var smallText = d.createTextNode("Smaller");
spanSmall.appendChild(smallText);
div.appendChild(spanBig);
div.appendChild(spanSmall);
d.body.appendChild(div);

spanBig.onclick = function() {
	resizeText(10);
}

spanSmall.onclick = function() {
	resizeText(-10);
}

function resizeText(multiplier) {
    if (d.body.style.fontSize == "") {
        d.body.style.fontSize = "100%";
    }
    d.body.style.fontSize = parseFloat(d.body.style.fontSize) + multiplier + "%";
}

spanBig.onkeypress = spanBig.onclick;
spanSmall.onkeypress = spanSmall.onclick;
</script>

</body></html>

Does anyone have any suggestions on how to tighten this up a bit? I really dislike having the two .onclick references. (I added a few extra bits to make it keyboard accessible, too, which is another requirement. I’d also love it if the natural browser resizing would still work, too—i.e. Command + 0 to reset the page. Hate the way JS disables normal browser bahavior—but then, I like to keep my cake and eat it too.)

Hi Ralph,

I’m on the hop right now, but just wanted to post this suggestion.
What you could do, is add a class to the span elements you want to target.
Then you could select all of the elements with this class, loop through them and reference the individual elements within the loop using this.
You could a data-attribute to the span elements as you create them, so that you know which argument to pass to the resizeText function.

I’ll have a closer look at your code when I’m back at the PC and see if I can help you out more.

I did play around with that, but got all messed up. Not really able to think in JS yet, other than “cat on mat” kind of stuff. :slight_smile: Keen to learn, though.

You could a data-attribute to the span elements as you create them, so that you know which argument to pass to the resizeText function.

Ah, that sounds interesting. I’d love to see that in action.

I’ll have a closer look at your code when I’m back at the PC and see if I can help you out more.

Many thanks. That would be great. :slight_smile: I’ve seen this kind of functionality so many times over the years, I’m surprised I didn’t stumble across an unobtrusive version. There are jQuery versions, of course, but I’d much rather learn JS without a library at this stage. And I’d really like to have the minimum code needed to do this. :slight_smile:

Hi Ralph,

I had a look at the script and have one major suggestion about what could be improved:

The main thing I try and avoid when writing JS is repetition (as you mentioned yourself).
In your version you create an element, do a bunch of stuff with it then create another element and do exactly the same stuff with it.
This isn’t so tragic for two elements, but would suck if you had to do it for two hundred.

So, my recommendation would be to stick all of this into a function.
Here’s your revised script:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>TextResize</title>
    <style media="all">
      body {font: 100% serif;}
      .buttons span {display: inline-block; padding: 20px; background: #f7f7f7; cursor:pointer}
      .buttons span:hover {background: orange;}
      .buttons span:focus {background: green; outline: none;}
    </style>
  </head>

  <body>
    <p>This is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text this is text</p>

    <script>
      function resizeText(multiplier) {
        if (d.body.style.fontSize == "") {
          d.body.style.fontSize = "100%";
        }
        d.body.style.fontSize = parseFloat(d.body.style.fontSize) + multiplier + "%";
      }

      function createSpan(b, v){
        el = d.createElement("span");
        el.setAttribute("tabindex","0");
        t = d.createTextNode(b);
        el.appendChild(t);
        div.appendChild(el);
        el.onclick = function(){
          resizeText(v);
        }
        el.onkeypress = el.onclick;
      }

      var d = document;
      var div = d.createElement("div");
      div.setAttribute("class","buttons");
      createSpan("Bigger", 10);
      createSpan("Smaller", -10);
      d.body.appendChild(div);
    </script>
  </body>
</html>

As you can see I have created a function called createSpan which takes two arguments:

  1. The text for the element
  2. The size by which the text should be decresed on click.

I’ve also used shorter variable names, as they are basically used, then thrown away and don’t pop up else where in the code.
This is just a personal preference.

This is by no means a definitive answer, so I would be glad to hear anyone else’s input on this.

Hope that helps.

Ooh, thanks so much, “Dave”. :smiley: That looks great—much more efficient. Exactly the sort of thing I was after. And I do like the shorter var names, too.

I’m going to pore over this and will let you know if I don’t understand any of it. It certainly makes a big difference to have a go first and then see a better way. :slight_smile:

I was looking at tightening up the first function, too. Still wondering if it can be done differently (it was just something I found on the web). I can see why you can’t just say

d.body.style.fontSize = "100%";

without the if, but was trying something like a += to keep incrememting it or something, but got nowhere with that.

Anyhow, just for fun, I screwed it down to this at least:

function resizeText(m) {
      var b = d.body;
        if (b.style.fontSize == "") {
          b.style.fontSize = "100%";
        }
        b.style.fontSize = parseFloat(b.style.fontSize) + m + "%";
      }

Wondering if something similar can be done with .style.fontSize.

You could omit the function entirely.

<script>
  function createSpan(b, v){
    el = d.createElement("span");
    el.setAttribute("tabindex","0");
    t = d.createTextNode(b);
    el.appendChild(t);
    div.appendChild(el);
    el.onclick = function(){
      d.body.style.fontSize = (d.body.style.fontSize == "")? "110%" : parseFloat(d.body.style.fontSize) + v + "%";
    }
    el.onkeypress = el.onclick;
  }

  var d = document;
  var div = d.createElement("div");
  div.setAttribute("class","buttons");
  createSpan("Bigger", 10);
  createSpan("Smaller", -10);
  d.body.appendChild(div);
</script>

That’s nice. :slight_smile: Although, the initial 110% seems initially to make Smaller increase the text size if it’s clicked first.

Really?
Not for me.

Your initial function checks if the body tag has a font-size style.
If not, it applies one of 100%, then increases it by 10%.

Could it be a MAC thing?

Hm, that’s odd. If the first thing I click with your revised code (post #7) is the Smaller button, the text initially resizes, then everything proceeds as normal. Would be odd if just a Mac thing, but happens in all the Mac browsers. (Seems to make sense, though: if there’s not a font size set, which is the case on the first click, everything goes to 110%, even if the Smaller button is clicked. Is that the right way to read the ternary operation?)

Anyhow, I also tried this, which is not as efficient, but seems to work OK:

<script>
  function createSpan(b, v){
    el = d.createElement("span");
    el.setAttribute("tabindex","0");
    t = d.createTextNode(b);
    el.appendChild(t);
    div.appendChild(el);
    var b = d.body;
    [COLOR="#FF0000"]b.style.fontSize = "100%";[/COLOR]
    el.onclick = function(){
      [COLOR="#FF0000"]b.style.fontSize = parseFloat(b.style.fontSize) + v + "%";[/COLOR]
    }
    el.onkeypress = el.onclick;
  }
  
  var d = document;
  var div = d.createElement("div");
  div.setAttribute("class","buttons");
  createSpan("Bigger", 10);
  createSpan("Smaller", -10);
  d.body.appendChild(div);
</script>

I’m just playing around now, but this is fascinating. Hard to do it without a guiding hand, though. I’m really loving your createSpan() function. :slight_smile:

Hey cool,

I like that more than my suggestion as ternary conditionals are always confusing to read.

Nice one!
:slight_smile:

Thanks. I didn’t expect it to work, as I did try a few things like that before, which all failed. Thanks to your code, though, I got to have another try. I expected you to say it was inefficient, though. :lol:

Just one thing I wondered. A few variables appear without being declared (is that the right terminology) and I’m sure I’ve read that it’s best to do this first. I’ve messed around with it a bit:


          [COLOR="#FF0000"]var el, d = document, bd = d.body, div = d.createElement("div");[/COLOR]
	  function createSpan(b, v){
	    el = d.createElement("span");
	    el.setAttribute("tabindex","0");
	    el.appendChild(d.createTextNode(b));
	    div.appendChild(el);
	    
	    bd.style.fontSize = "100%";
	    el.onclick = function(){
	      bd.style.fontSize = parseFloat(bd.style.fontSize) + v + "%";
	    }
	    el.onkeypress = el.onclick;
	  }
	  
	  div.setAttribute("class","buttons");
	  createSpan("Bigger", 10);
	  createSpan("Smaller", -10);
	  d.body.appendChild(div);

Often I see the variable declared (or even redeclared) inside the function where it actually appears. Does it matter where it appears?

Hi Raplh,

Yup, it is the correct terminology.

This is all a question of scope (and my bad programming style :)).
A small example:

var g = "global";
function go() { 
 var l = "local";
}
console.log(g);
go();
console.log(l);

outputs:

global
Uncaught ReferenceError: l is not defined 

Using var limits the variable l to the current scope, i.e. that of the function go().

However, if we omit var:

var g = "global";
function go() { 
 l = "local";
}
console.log(g);
go();
console.log(l);

outputs:

global
local

This is because the variable bubbles up through the layers of scope until it encounters a variable by the given name or the global object (window, if you are doing it in the browser), where it then attaches.

To come back to your question:
It is good practice to declare variables with var before using them (shame on me).
It also matters where you declare them (as we saw above), as the scope of variables declared using var within a function are limited to that function.

Here’s the most recent iteration of your code, revised:

function createSpan(b, v){
  var el = d.createElement("span");
  el.setAttribute("tabindex","0");
  el.appendChild(d.createTextNode(b));
  div.appendChild(el);
  
  bd.style.fontSize = "100%";
  el.onclick = function(){
    bd.style.fontSize = parseFloat(bd.style.fontSize) + v + "%";
  }
  el.onkeypress = el.onclick;
}

var d = document, bd = d.body, div = d.createElement("div");
div.setAttribute("class","buttons");
createSpan("Bigger", 10);
createSpan("Smaller", -10);
bd.appendChild(div);

You might also find the following interesting:

Smashing Magazine - What You Need To Know About JavaScript Scope
StackOverflow - JavaScript Variable Scope
StackOverflow - Javascript: is using ‘var’ to declare variables optional?

Thanks @Pullo

I have read about scope, but it goes in one ear and out t’other until I need to know. :smiley: At least I remembered the recommendation to use var, though I couldn’t remember why. Thanks for the very clear examples and explanation. (Better than in the books. :wink: )

Thanks for the links, too. :slight_smile:

I still find it funny how you can declare variables after you see them in a function, but I guess the browser reads those declarations before the function is actually invoked (if that’s the right word), so I guess I’m getting the conepts slowly. On more complex scripts, I get quite befuddled about the order of everything and what calls what is which order … Still dunno how people write that stuff!