Progressively Enhanced Transcripts With jQuery

Posted By : todd sharp Posted At : January 15, 2009 10:08 AM Posted In: jQuery, Ajax, Usability, JavaScript, SlideSix

3

I quietly added a new feature to SlideSix earlier this week which now extracts the text contents of each slide and displays that text as a transcript on the view page of a presentation. The reason behind this enhancement is to dramatically increase the search engine visibility of your presentations since search engines will be basically indexing the content of each of your slides.

I'm not 100% sure the current incarnation is going to last or if I will change it up a bit, but I wanted to blog about it because I think some of the code involved is pretty cool. First off, the main priority is that search engines see and index the text. Simple enough, just output it directly on the page, right? Well, yeah, but just outputting a giant list of text can be a bit bland and uninteresting if someone were to actually want to read the transcript. However, if I were to get fancy with the UI and hide a bunch of text I'm afraid search engines won't pick it up. What I needed was a progressively enhanced version. The text dump to the page would be picked up by the search engines (and those users without JavaScript enabled) and would look like such: transcript raw And through a bit of jQuery magic, users with JavaScript enabled will get the following version which in my opinion is a bit easier on the eyes and more 'fun' to navigate. transcript enhanced So let's take a quick look at some of the code. Like just about everything, jQuery makes this painstakingly simple. First off I have a simple div with an id of 'transcriptRaw' that wraps the entire text based transcript. Each slide is nested in the raw structure like so:
<div id="transcriptRaw">
    <h4 class="rawSlideTitle" id="title_CF1FB601-FF9E-13A7-FC7A4482268FFD2E"><strong>HELICOPTER VIEW OF THE APPROACH FOR RSS</strong></h4>
    
    <p class="homeText">
        <span id="showSlideText_CF1FB601-FF9E-13A7-FC7A4482268FFD2E" class="fakea textToggle nodisplay"><em>No Text Available</em></span>
        <span id="showSlideNotes_CF1FB601-FF9E-13A7-FC7A4482268FFD2E" class="fakea notesToggle nodisplay"><em></em></span>
    </p>
    <div class="slideText" id="slideText_CF1FB601-FF9E-13A7-FC7A4482268FFD2E">
        <p class="homeText transcriptBg"><em>no text exists for this slide</em></p>
    </div>
    <div class="slideNotes" id="slideNotes_CF1FB601-FF9E-13A7-FC7A4482268FFD2E">
        <p class="homeText transcriptBg"><em>no notes exist for this slide</em></p>
    </div>
</div>
Nothing too special going on there really. So to facilitate the enhancement I created a skeleton structure that would ultimately house the final enhanced version.
<div id="transcriptEnhanced" class="solidBorder nodisplay">
    <div id="slideList" class="float-left solidBorder slideList"></div>
    <div id="slideTextAndNotes" class="float-left solidBorder slideTextAndNotes">
        <p id="slideTextOuter">
            <img id="slideThumbnail" src="/images/no_slide.jpg" alt="no image" align="right" />
            <h4>Slide Text</h4>
            <p id="slideTextInner"></p>
        </p>
        <div id="slideNotesOuter">
            <h4>Slide Notes</h4>
            <p id="slideNotesInner"></p>
        </div>
    </div>
    <div class="clear"></div>
</div>
This structure is initally hidden via CSS (display: none). So as I stated above, users without JS (or spiders) will see the raw version, but users with JS will see the enhanced version. The enhanced version is built with a few lines of jQuery (heavily commented to show what is going on):
$(document).ready(function(){

    //hide the raw version

    $('#transcriptRaw').hide();
    
    
    //build the slide title list

    $('.rawSlideTitle').each(function(){
        var thisid = $(this).attr('id').split('_')[1];
        var newEntry = '<div class="pointer slideListEntry" id="slideTitle_' + thisid + '">' + $(this).text() + '<\/div>';
        $('.slideList').append(newEntry);
    });
    
    //highlight the rows as the user mouses over them

    $('.slideList').children().hover(
        function(){
            $(this).addClass('hover');
        },
        function(){
            $(this).removeClass('hover');
        }
    );

    //the real 'magic' -- when a slide title is clicked, get the text and notes and display them

    $('.slideListEntry').click(
        function(){
            //used to construct the path to the thumbnail

            var alias = 'TXP912RSS';
            
            //remove the selected class from all of the other slide titles

            $('.slideList').children().removeClass('select');
    
            //add the selected class to the current slide title

            $(this).addClass('select');
            
            //set up some vars to tell us where the text and notes, etc are in the raw structure

            var slideid = $(this).attr('id').split('_')[1];
            var slideTextID = '#slideText_' + slideid;
            var slideNotesID = '#slideNotes_' + slideid;
            var slideText = jQuery.trim($(slideTextID).text());
            var slideNotes = jQuery.trim($(slideNotesID).text());

            //reset the scroll position of the notes div (yeah, i could have done this with jQuery too)

            document.getElementById('slideTextAndNotes').scrollTop = 0;

            //load the image (thumbnail) up

            var iPath = '/presentations/' + alias + '/thumbs/' + slideid + '.jpg';
            $('#slideThumbnail').attr('src', iPath).error(function(){
                $(this).attr('src', '/images/no_slide.jpg');
            });
            
            //display the text and notes

            if(slideText.length >
0){
                $('#slideTextInner').html(slideText);
            }
            else{
                $('#slideTextInner').html('text not available');
            }
            if(slideNotes.length > 0){
                $('#slideNotesInner').html(slideNotes);
            }
            else{
                $('#slideNotesInner').html('notes not available');
            }
        }
    );

    //on load select the first slide

    $('.slideListEntry:first').triggerHandler('click');
    
    //show the enhanced version

    $('#transcriptEnhanced').show();
    
});
So hopefully that code is self-explanatory with the comments. I want to especially highlight a really cool line.
//load the image (thumbnail) up

var iPath = '/presentations/' + alias + '/thumbs/' + slideid + '.jpg';
$('#slideThumbnail').attr('src', iPath).error(function(){
    $(this).attr('src', '/images/no_slide.jpg');
});
This line tries to load up the thumbnail, and if it doesn't exist for whatever reason it triggers the error() handler which loads my default 'no image available' image. Thanks to Rob Gonda's post for pointing out the fact that images actually fire the onError when the src img doesn't exist. One friend has mentioned that the thumbnail really shouldn't be there since it is technically a text transcript. What do you think? Any other suggestions on the feature? Note: Transcript support is only applicable to new presentations that are uploaded, and does not include the QuickTime (MOV) format. Also, for what should be obvious reasons, any text contained within images is not extracted or indexed.

Comments (3)

Gary Gilbert's Gravatar Hi Todd,

Wouldn't it make more sense to hide the raw version and show the enhanced version first?

You could then override your nodisplay tag by wrapping another style block with noscript tags.

As soon as a spider or user comes without js enabled your noscript tag is automatically fired.

Just an idea

todd sharp's Gravatar Interesting, but I'm not sure I follow you 100%. Mind sketching up some pseudo code for how you'd go about that. Also, what advantage would you say that gives me?

I've always thought of PE as building for the lowest common denominator and then upgrading it, so that's why I went with this method.

Gary Gilbert's Gravatar Hi Todd,

Well firstly the lowest common denominator in this case is not a spider or a user without javascript. What you really want, if I may be so bold, is that the content is indexed by spiders, and secondarily provide a means for those less fortunate (javascript-less) to actually use the site.

I like to build for the majority and degrade gracefully. So you put your best forward first and then when a spider or a user without javascript comes you degrade to something they can see.

You could put something similar into the head of your document after you have included all your css

&lt;noscript&gt;
&lt;style&gt;
.nodisplay{
visibility:visible
}
&lt;/style&gt;
&lt;/noscript&gt;

That would only override your existing class definition if javascript is disabled.

And with having the raw visible first and then through javascript hiding it you run the risk, in slower browsers, of having the raw be on the screen for a "second" before the javascript hides it.