Posts Under Tag: JavaScript

Interacting With The YouTube iFrame API

YouTube Player APIs

Displaying a YouTube video within a web app is easy; controlling events like starting the video and detecting when it ended gets a little more involved.  Luckily YouTube has player tools that include Player APIs.  For web applications there are currently three Player API choices: the IFrame API; the JavaScript API; and the Flash API (AS3).  These APIs allow the consumer fine-grained control over the Player including the visibility of Player controls, configuring the Player size, and perhaps most importantly, listening to Player events such as start, stop, etc.  All of these exposed items can be seen in action in the YouTube Player Demo.

Choosing a Player API

I was developing a site where it was required that the user watched a specified YouTube video through completion until being allowed to continue on to another page of a web site.  To get started, I had to choose what YouTube API library would work best in this situation.  Given that I couldn’t expect all end users to have Flash installed, I knew right away that the Flash API was out.  Flash is out the door as a technology and most mobile devices don’t support it in favor the HTML5 player.

The IFrame Player API

With the Flash API out, this leaves the iFrame API and the JavaScript API.  Given its name, one may assume that the JavaScript API is the most modern API and may provide HTML5 capabilities, but it doesn’t.  Listed in the requirements for the YouTube JavaScript API is:  “The end user must have Flash Player 10.1 or higher installed to view everything correctly.”.  So that leaves the IFrame API.  To me, the use of an IFrame seems a bit dated, but the API name is sort of a misnomer.  It’s in fact the newest YouTube Player API and it is completely controllable via – you guessed it, JavaScript!

True to its name, the YouTube Player IFrame API does post content to an <iframe> tag on the page that serves an HTML5 player or Flash player depending on the capabilities of the client browser.  It is very simple to use and the documentation even has a great getting started guide complete with a working code sample:

<!DOCTYPE html>
<html>
  <body>
    <!-- 1. The <iframe> (and video player) will replace this <div> tag. -->
    <div id="player"></div>

    <script>
      // 2. This code loads the IFrame Player API code asynchronously.
      var tag = document.createElement('script');

      tag.src = "https://www.youtube.com/iframe_api";
      var firstScriptTag = document.getElementsByTagName('script')[0];
      firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

      // 3. This function creates an <iframe> (and YouTube player)
      //    after the API code downloads.
      var player;
      function onYouTubeIframeAPIReady() {
        player = new YT.Player('player', {
          height: '390',
          width: '640',
          videoId: 'M7lc1UVf-VE',
          events: {
            'onReady': onPlayerReady,
            'onStateChange': onPlayerStateChange
          }
        });
      }

      // 4. The API will call this function when the video player is ready.
      function onPlayerReady(event) {
        event.target.playVideo();
      }

      // 5. The API calls this function when the player's state changes.
      //    The function indicates that when playing a video (state=1),
      //    the player should play for six seconds and then stop.
      var done = false;
      function onPlayerStateChange(event) {
        if (event.data == YT.PlayerState.PLAYING && !done) {
          setTimeout(stopVideo, 6000);
          done = true;
        }
      }
      function stopVideo() {
        player.stopVideo();
      }
    </script>
  </body>
</html>

However, not all samples fit all situations.  This sample defines an OnReady event on line 24 that in turn defines a  function named OnPlayerReady.  That OnPlayerReady function is using a command on line 32 to play the video.  This is setting up the video to autoplay when the page loads.  This works great unless you are on an iOS or modern Android device.  Apple specifically disallows autoplay of audio and video on iOS devices in an effort to preserve any of the user’s possible data usage limitations.  What you instead get is the YouTube Player loads and then the screen goes black and no playback controls are visible.

Special Considerations for iOS and Android

Given that attempting to autoplay a video on iOS or Android causes the player to enter an error state, there’s a need to detect if the client’s user agent is iOS or Android and not autoplay the video.  The user will instead have to push the play button on the YouTube Player.  This can be accomplished by modifying the OnPlayerReady function to detect this:

function onPlayerReady(event) {
    var iOS = /(iPad|iPhone|iPod|Android)/g.test(navigator.userAgent);
    if (!iOS) {
        event.target.playVideo();
    }
}

Detecting When The Video Has Ended

In this sample, the onPlayerStateChange function is assigned to the Player’s onStateChange event.  It is here where player events are captured and can be put to use.  In my case, there was a requirement to somehow track that the user had watched the video to completion in order to visit a certain other page.  This can be accomplished by first creating a session cookie when the player detects the video has Ended.

var done = false;
function onPlayerStateChange(event) {
    if (event.data == YT.PlayerState.ENDED && !done) {
        // create session cookie indicating video watched
        document.cookie = "trackview=watched; expires=0; path=/" // using expires=0 sets a session cookie
        done = true;
    }
}

Verifying The Video Was Watched

At this point the iFrame Player is capturing the Ended state of the video and then issues a session cookie.  Now on the page that needs to ensure that the cookie exists, a check can be placed on its window.onload event:

function checkCookie() {
    var x = readCookie('watched');
    if (!x) {
        document.location.href = "/";
    }
}

window.onload = checkCookie;

Here if the cookie doesn’t exist, the user is being sent back to the site root.  If the cookie does exist a general assumption can be made that the user was issued the cookie when the video was watched to completion, thanks to the YouTube IFrame API event that we subscribed to.

Tags: Filled Under: Programming Posted on: August 11, 2014

Word Count Limiter in JavaScript

Character counts for text is easy; word counts get a bit more subjective.  This can be accomplished with a bit of JavaScript/jQuery and a regular expression.

Google for regular expressions like this and you’re likely to find a vast amount of complicated expressions.  Be careful of these unless you have a specific business need for a complicated regular expression.  I found many counted certain punctuation within a word or adjacent to it as a separate word.  I found a great JSFiddle that contains the simplest way to count words – just look for the spaces between characters.

var regex = /\s+/gi;
var wordCount = value.trim().replace(regex, ' ').split(' ').length;

The regex variable is a regular expression that looks for spaces, ignores casing, and performs a global search.  Want to verify that?  Test it out in this RegEx tester.  The wordCount variable trims the empty space outside of the string, replaces any internal whitespace with a single space, and then counts the number single spaces.

Now that you have a way to identify words and count them, the next step is to limit the text box to a certain number of words.  Let’s start with a multi-line text box and word counter:

<textarea id="text-input" cols="25" rows="3"></textarea> 
<div class="word-counter">
	Word Count: <label id="count-label">0</label>/10
</div>

Using a bit of jQuery, you can then count the number of words, update the counter, and limit the textarea input to a maximum number of words (in this case 10 words).

var limitWord = 10;
$("#text-input").keyup(function () {
    $this = $(this);
    var regex = /\s+/gi;
    var wordcount = jQuery.trim($this.val()).replace(regex, ' ').split(' ').length;
    if (wordcount <= limitWord) {
        chars = $this.val().length;
    } else {
        var text = $(this).val();
        var new_text = text.substr(0, chars);
        $(this).val(new_text);
        wordcount --;
    }
    $("#count-label").html(wordcount);
});

The result is:

Word Count Limiter

A working sample can be found here, on JSFiddle: http://jsfiddle.net/justinsaraceno/ezZxf/

Tags: Filled Under: Programming Posted on: October 20, 2013

Book Review: JavaScript Enlightenment

JavaScript Enlightenment by Cody Lindley is billed as being targeted toward developers who are familiar with JavaScript at an advanced-beginner level or someone who’s only exposure to JavaScript is through a wrapper framework like jQuery.  Its goal is to provide the reader with the core fundamentals of JavaScript in order to allow you to later advance your JavaScript proficiency.  Considering myself to be at that level of understanding, I found this book to be appealing.

Javascript Enlightenment book

JavaScript Enlightenment

The book starts out by thoroughly exhausting the topic of JavaScript objects.  In fact, this topic alone takes up almost half the book, and it becomes apparent that it does so deservingly.  The book’s unique style of presentation is to introduce a tightly-scoped concept with a brief discussion and then show a related working sample.  After exhausting the topic of objects, the next topic it tackles is  functions.  Again, the author leaves no stone unturned while demonstrating how functions behave in JavaScript.  Everything from scope, to nesting, to recursion, to inheritance, and beyond is covered in this short book.

The book’s title could have easily been named, “JavaScript Fundamentals That You Absolutely Need to Know in Order to Become Better at JavaScript”.  JavaScript Enlightenment provides necessary core details and presents them in a way that feels like it is hammering them in to your brain.  I believe it does that because the concepts are broken down in to such minutely detailed segments with accompanying samples, that your mind never gets a chance to ‘drift’ from the lesson being taught.

Overall, once you get past the unique presentation style of the book and realize that you truly are learning the fundamental concepts necessary to advance in JavaScript, the book becomes easier to read.  Anyone who falls in to the intended audience (advanced-beginner level) should feel comfortable moving on to more advanced JavaScript topics like designs, patterns, and best practices after reading this book.  The end of the path for someone seeking enlightenment takes the seeker to a (hopefully) better state of mind and understanding, and that’s exactly what I feel JavaScript Enlightenment accomplishes.  Disclaimer Note: As a user group leader I requested a complimentary e-book copy from the publisher for this review; my review is true and I stand to make no monetary gain whatsoever from the sales of this book.

Tags: , Filled Under: Programming Posted on: February 20, 2013

Common JavaScript Pitfalls with Internet Explorer

Lately, more and more .NET developers seem to be have taken an interest in sharpening their JavaScript skills.  I see evidence of this at the local .NET user meetings I attend.  Whenever a topic is related to mobile development, HTML5, JavaScript, or jQuery, the room is packed.  It’s no surprise really.  All those topics are in hot demand now and I can only imagine their growth will continue as areas like mobile development grows, Flash dies, and Windows 8 development gains interest.

I’ve been using JavaScript for a long time but never considered myself any type of authority on it.  Recently  I’ve been growing my JavaScript and jQuery skills.  I find it challenging and enlightening to do things purely client-side that just a few years ago I would have done in ASP.NET with things like postbacks and update panels.  However, even with libraries like jQuery floating around that do their best to be compliant with all browsers, you still have to watch out for some browser-specific nuances.

Take for instance, this simple html page:

<!DOCTYPE html>
<html>
    <head>
        <title>JavaScript Test</title>
    </head>
    <body>
        <a href="#" id="toggle">Click to Toggle</a>
        <div class="content">Hello from the toggle div!</div>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
        <script type="text/javascript">
            $(document).ready(function () {
                $("#toggle").click(function () {
                    console.log("toggle clicked");
                    $(".content").toggle();
                    return false;
                });
            });
        </script>
    </body>
</html>

Forget the fact that the JavaScript isn’t in a separate file, and maybe some other best practices that I may be breaking.  Here’s an explination of what’s going on.  In line 7 I have an href tag that is acting like a toggle button for the visibility of the div on line 8.  The function on line 12 wires up the click event for the href.  On line 13 I’m doing a console.log to debug that I’m in the click event.  Line 14 is toggling the visibility of the div.

So basically, I have a div and clicking on the href toggles its visibility.  Here is the div loaded up in Chrome with the Chrome developer tools (F12) turned on:

When the ‘Click to Toggle’ link is clicked, the “Hello from the toggle div!” text disappears and the console logging event shows up in the console. [if your’re unfamiliar with console.log, think of it as the modern alternative to debugging your JavaScript with alerts].

Excellent!  We’re ready to push this out production now, right?  Well, maybe if all your users were on Chrome, Firefox, or Safari.  If they use IE9, this won’t run without changing one minor thing.  If they run IE 7 or 8, this won’t run without doing even more work.  Yes, IE is crippled and even these few lines of code outwit Internet Explorer.

console.log can break IE

It turns out that in IE, if you don’t actually have the IE developer tool console open (press F12), then a call to console.log will cause an exception and your script will not run.  This doesn’t happen in the other major browsers, they just roll on past.

Thanks to a great question I found on Stack Overflow, I learned the answer is to first check if the console is available, then call console.log:

if (window.console) {
    console.log("toggle clicked");
};

Why doesn’t the JavaScript .click() event work on IE 7&8?

Back in the function, in order to have the href show and hide the div, a click() event was wired up.  When the click event is called, line 15 explicitly returns false to prevent any default behavior of the calling button.  If this was a long scrolling page, the click event would take the user back to the top of the page if the return false wasn’t there.  Having the event return false prevents that behavior and the scrolling position is maintained… except of course in older versions (7 and below) of IE.

To get these older version of IE to preform the click() event properly, the click event has to be modified:

$("#toggle").click(function (e) {
    e.preventDefault();
                ...

Notice that the click event accepts a parameter ‘e’ which is the event.  The event then has to explicitly prevent the default click behavior by calling preventDefault().

Putting it All Together

The final IE-safe script will then look like this:

$(document).ready(function () {
    $("#toggle").click(function (e) {
        e.preventDefault();
        if (window.console) {
            console.log("toggle clicked");
        };
        $(".content").toggle();
        return false;
    });
});

Here, the default behavior of the click event is explicitly prevented and the presence of the console is checked before using the console.  I learned all this the hard way, but enjoyed learning it all the same.  These may seem like some elementary concepts to anyone who has been working with JavaScript for a while.  However, for those like myself who have spent time away from the language, you’re sure to run in to these types of issues while (re)learning.

Tags: Filled Under: Programming Posted on: August 17, 2012