Monday, March 9, 2015

Automatically adding captions to images on Blogger

One of the advantages of using the Blogger web interface is that knowledge of HTML isn’t required. Images that get added through the rich text interface are automatically inserted into a “div” container and hyperlinked so that clicking on them starts a slideshow of the page’s images. Captioned images are wrapped in a table. The user doesn’t need to know any of this since it’s done for them behind the scenes.

Good practice dictates that images have “alt” attributes assigned to them. The “alt” attribute is used in HTML and XHTML documents to specify alternative text (a.k.a. alt text) that is to be rendered when the element to which it is applied cannot be rendered. It is also used by “screen reader” software so that a person who is listening to the content of a webpage (for instance, a person who is blind) can interact with this element. Another attribute commonly used is “title.” Assigning this attribute allows for a pop-over to appear when the mouse pointer is hovering over an image. This can be useful way to provide supplemental information or context about the image that the author feels isn’t important enough to include as a caption.

The advent of touch interfaces makes hovering impossible. Mobile devices almost always implement a touch interface, so mobile users cannot access the title attribute of an object. To assist mobile users without putting an additional burden on a blogger, I’ve come up with a way to automatically append the title attribute text as a caption to images for mobile browsers. No intimate knowledge of HTML or JavaScript is necessary; however, this process will require changes to the Blogger template and style sheet.

Go-Go Gadget!


Rather than inserting new JavaScript code directly into the raw HTML of the Blogger template, we can add it as a new gadget on the blog’s layout. Go to the “Layout” screen and add a new gadget to the bottom of the page. Look for the “HTML / Javascript” one and click on the “+” to add it to the layout.

HTML/JavaScript gadget.

Set the gadget’s title to “Mobile Image Captions” and copy and paste the following JavaScript into the content window:

<script language="javascript" type="text/javascript">
//<![CDATA[
/* Based on scripts from:
   http://askthecssguy.com/articles/image-captions-generated-with-css-and-javascript/
   http://javascript.info/tutorial/onload-ondomcontentloaded

   Edit blogger template to use this JavaScript gadget for mobile only:
   http://blogger-hints-and-tips.blogspot.com/2012/05/add-gadget-to-mobile-template-for-your.html
*/

function captionizeImages() {
  if (!document.getElementsByTagName) return false;
  if (!document.createElement) return false;
  var images = document.getElementsByTagName("img");
  if (images.length < 1) return false;
  for (var i = 0; i < images.length; i++) {
    var title = images[i].getAttribute("title");

    // only process images that have a non-empty title attribute
    if (title && title.trim() != '') {
      var parent = images[i].parentNode;

      // check if image parent is hyperlink and go up one more level
      if (parent.tagName == 'A') parent = parent.parentNode;

      // we're only looking for images in the div wrapper or table from the blogger editor
      if (parent.className == 'separator'
          || parent.parentNode.parentNode.parentNode.className == 'tr-caption-container') {
        // copy title attribute from image and put in a div following the image
        var divCaption = document.createElement("div");
        divCaption.className = "caption";
        var divCaptionText = document.createElement("div");
        divCaptionText.className = "caption-text";
        var divCaptionText_text = document.createTextNode(title);
        divCaptionText.appendChild(divCaptionText_text);
        divCaption.appendChild(divCaptionText);
        parent.appendChild(divCaption);
      }
    }
  }
}

function bindReady(handler) {

  var called = false;

  function ready() {
    if (called) return;
    called = true;
    handler();
  }

  if (document.addEventListener) { // native event
    document.addEventListener("DOMContentLoaded", ready, false);
  } else if (document.attachEvent) { // IE

    try {
      var isFrame = window.frameElement != null;
    } catch (e) {}

    // IE, the document is not inside a frame
    if (document.documentElement.doScroll && !isFrame) {
      function tryScroll() {
        if (called) return;
        try {
          document.documentElement.doScroll("left");
          ready();
        } catch (e) {
          setTimeout(tryScroll, 10);
        }
      }
      tryScroll();
    }

    // IE, the document is inside a frame
    document.attachEvent("onreadystatechange", function() {
      if (document.readyState === "complete") {
        ready();
      }
    });
  }

  // Old browsers
  if (window.addEventListener)
    window.addEventListener('load', ready, false);
  else if (window.attachEvent)
    window.attachEvent('onload', ready);
  else {
    var fn = window.onload; // very old browser, copy old onload
    window.onload = function() { // replace by new onload and call the old one
      fn && fn();
      ready();
    };
  }
}


var readyList = [];

function onReady(handler) {

  function executeHandlers() {
    for (var i = 0; i < readyList.length; i++) {
      readyList[i]();
    }
  }

  if (!readyList.length) { // set handler on first run 
    bindReady(executeHandlers);
  }

  readyList.push(handler);
}

onReady(function() {
  captionizeImages();
});

//]]>
</script>

You should immediately see the title text appearing in an unformatted manner after your images providing you had set title attributes in the images’ properties.

Enable the gadget for mobile users


We want this gadget just for mobile users, but ironically, the gadget is probably disabled in the mobile template. We can fix this by first ensuring the mobile template is enabled. Go to the “Template” link as shown below.

Template screen.

Click on the gear icon under the mobile template. Choose “Yes. Show mobile template on mobile devices.” and click the “Save” button. Click on the “Edit HTML” button for the main template. This will show you the raw HTML code for the template. Don’t be scared! Scroll down all this gobbledygook to the bottom and look for the gadget by scanning for the “Mobile Image Captions” title you gave the gadget.

<b:widget id='HTML2' locked='false' title='Mobile Image Captions' type='HTML'>

Insert mobile='yes' into the tag:

<b:widget id='HTML2' locked='false' mobile='yes' title='Mobile Image Captions' type='HTML'>

Click “Save template” and then “Back.” This will enable the gadget for both templates.

Format the caption


We now need to edit the template’s style sheet to format the caption. Click on the Template link (shown in the image above) and click on the “Customize” button. Click on “Advanced” and “Add CSS” on the Blogger Template Designer:

Blogger Template Designer.

Scroll to the bottom of the “Add custom CSS” window (assuming there’s something there already) and copy and paste the following:

.post-body .separator .caption, .post-body .tr-caption-container .caption
{
  display: none;
}

.mobile .post-body .separator .caption, .mobile .post-body .tr-caption-container .caption
{
  display: block;
}

.mobile .post-body .separator .caption .caption-text, .mobile .post-body .tr-caption-container .caption .caption-text
{
  display: inline-block;
  background-color: #fff9bd;
  border: 1px solid #cccccc;
  font-size: 0.7em;
  text-align: left;
  max-width: 80%;
  margin-left: auto;
  margin-right: auto;
  padding: 0.4em 0.4em 0.4em 0.4em;
}

This will not only format the caption, but it will also hide it for the non-mobile users. You can see what mobile browsers will display by adding ?m=1 to the url of a blog entry in the browser window.

Only use the gadget on mobile template


This final step is optional, but you can go back into the raw template HTML and change the mobile parameter to mobile='only' to enable the gadget only for the mobile template:

<b:widget id='HTML2' locked='false' mobile='only' title='Mobile Image Captions' type='HTML'>

Click “Save template” and then “Back.” The caption is already hidden thanks to the custom style sheet addition, but this goes a further step of preventing the JavaScript from being included and executed on the standard template. The downside to doing this will be that the gadget will no longer appear on the Layout screen unless the mobile='only' parameter is switched back to mobile='yes'.

Technical stuff


I’m including this information for reference in case anyone wants to better understand how this works. Images inserted through the Blogger rich text web interface are automatically wrapped in a <div> block and hyperlink:

<div class="separator" style="clear: both; text-align: center;">
  <a href="url" imageanchor="1" style="margin-left: 1em; margin-right: 1em;">
    <img alt="alt text" border="0" src="url" title="title text" />
  </a>
</div>

Images with a caption are wrapped in a table:

<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;">
  <tbody>
    <tr>
      <td style="text-align: center;">
        <a href="url" imageanchor="1" style="margin-left: auto; margin-right: auto;">
          <img alt="alt text" border="0" src="url" title="title text" />
        </a>
      </td>
    </tr>
    <tr>
      <td class="tr-caption" style="text-align: center;">
        Caption text.
      </td>
    </tr>
  </tbody>
</table>

The JavaScript function captionizeImages() finds all of the images on the page and adds a caption providing the following conditions are met:
  • It’s wrapped in a <div> block whose class is “separator” or in a table whose class is “tr-caption-container.”
  • It has a “title” attribute that contains at least one non-whitespace character.
Images that do not meet these conditions will not have a caption inserted. This may be desirable for certain kind of images added to a blog post. It’s up to the user to understand how to edit the HTML to accomplish this.

The title text is appended below the image in nested <div> blocks. A check is made to ensure the caption blocks are added after the hyperlink and not inside. It’s possible that a user might dig into the HTML source to remove the hyperlink if he or she wishes to add an image, but does not desire for it to be part of a click-able slideshow. The script will accommodate this choice.

The outer caption div block is assigned a class of “caption” and the inner with “caption-text.” The reason for the nesting is to allow the inner div to conform to the width of the text and be centered on the screen while the outer div block ensures the caption doesn’t end up on the same line as the image above. This is controlled through the custom style sheet entries. We could have applied the style information through the JavaScript, but keeping control in the master style sheet is a better practice.

No comments:

Post a Comment