Upcoming LunaMetrics Seminars
Pittsburgh, Jan 12-16 Boston, Jan 12-16 New York City, Jan 26-30 Denver, Feb 9-13

Easily Track YouTube Videos with Google Analytics

Updated as of January 23rd, 2014 with version 6

THE PROBLEM!

I WANT TO TRACK ALL MY YOUTUBE VIDEOS EMBEDDED ON MY PAGE WITHOUT HAVING TO WRITE NEW JAVASCRIPT EVERY TIME I EMBED A VIDEO!

Recently I was tasked with tracking YouTube videos on a website using Events in Google Analytics, and I was not particularly thrilled with the options. Either old blog posts from even our site, or posts that didn’t lay out any specifics on how to do it, or posts that listed basically how the YouTube player API instructs people to track videos. Most from a year or even two ago. Which was all well and good, until I realized that I was dealing with possibly hundreds of pages, with a variable amount of videos on them.

The basic YouTube IFrame Player API shows an example of one video. And if you add a second? Well you need to add more javascript, as well as modify your HTML. If you had 10 videos on a page you would have to modify the javascript for the page to create 10 different objects, and you’d need to replace or edit the embed code to div tags. Not exactly easy, particularly for someone to go back to a site already in existence.

What I wanted was basically, maybe, a javascript file you could include on your page and then just BAM presto chango, all the basic embeded YouTube videos would be tracked, no muss no fuss, no additional JavaScript. Write a new blog post, add a new YouTube video, and have the tracking just automatically happen. I couldn’t find that.

So I wrote it.

See that video? I embedded it with standard Youtube embed code. Copy and Paste into the blog. No additional javascript. Go ahead and view the source. I’m tracking when it buffers, plays, cues, pauses, or if someone watches it to the end. All I did was copy and paste the embed code straight from YouTube on the page.

This one too. Copy. Paste. Tracked with Events in Google Analytics. Easy as pie. A monkey could do it. Well… a trained monkey.

And this one too…Don’t believe me? Use your favorite tool, and check out the source. Just iframes. Check out the Network and see the UTM gif’s flying with Event’s being Tracked for being Rick Rolled straight to our Google Analytics account.

It’s ok. You can go ahead and slow clap.

THE SOLUTION!

INCLUDE OUR SCRIPT. THE END!

Here’s what you do. No complicated coding. Just download this file from our github, and include it in the head of your html. Just use one of them. We’ve updated this code a few times, so be sure to go here for the latest version.

You’ll also need Jquery so be sure to include that before the script. So you’ll have something like this

<script src=”//code.jquery.com/jquery-1.7.2.js”></script>

<script type=”text/javascript” src=”http://www.yourwebsitehere.com/js/lunametrics-youtube-v5.js”></script>

And then… That’s it.

UPDATE: NOPE... YouTube has updated their scripts so that you are now REQUIRED to have enablejsapi=1 attached to each video to be tracked. So you should probably manually add these.  You can also add this automatically with the reloadFrames variable included in the script, which you can change to be 1 instead of 0, but this will cause screen “flicker” but if you have 100s of pages and videos to update it could be a better short term fix. You can also instead manually add enablejsapi=1 to ever source of the videos as a parameter. END UPDATE.

The script will use jquery to scan through your page and look for iframes. Any iframe that has a standard youtube value in it’s source parameter like http://www.youtube.com/embed/ or https://www.youtube.com/embed/ will get acknowledged as a YouTube video. (if it has the -nocookie listing that is an option in Youtube we don’t track it, as it wouldn’t track anyway). Then we dynamically create the objects based on the YouTube ID’s based on their IFrame Player API and voila. All the iframes embedded on the page with the standard default YouTube embed code, as well as their https secure version, get tracked with Events in Google Analytics.

NOTES!

JUST BECAUSE IT’S NOT PERFECT!

1) It requires jquery as noted above. If you don’t have jquery on the page as well as our youtube code, it won’t work.

2) It assumes that the video is embedded only once on the page. You can have 100 videos on the page and it’ll track them all, but if you embed the same video twice it’ll actually error out because I’m using the YouTube video ID as the id for the player itself, so it’ll just overwrite itself, and the second video with the same YouTube video ID won’t get tracked.

3) I mentioned it before, but if you choose the option for “enhanced privacy mode” which sets a no cookie field in the URL, we can’t track those.

4) We track Play, Pause, Cue, Buffer, and Watch till End, but you’re free to add additional stuff to our code for your own use.

5) We originally stripped the YouTube ID out from the URL and that’s what got placed into the Event for identification, not the full URL. The new code uses a separate api to grab the title of the video and has the event read “Title of the Video | ASdfj0oi2_34vh” etc where the garbage is the YouTube ID. This hopefully makes it easier to read. You can change this setting in a variable early in the code to display just the title, or just the id. It defaults to concatenated.

6) This works with the embed code YouTube provides. If you copy the URL and gum up the url in the iframe with all sorts of other parameters that aren’t standard, it probably won’t work either. It’ll work with URL’s like http://www.youtube.com/embed/e90s3v_G4PU?rel=0 or http://www.youtube.com/embed/e90s3v_G4PU or https://www.youtube.com/embed/e90s3v_G4PU?rel=0 or http://www.youtube.com/embed/e90s3v_G4PU which are the basic forms that YouTube provides as the SRC value in the iframe. It’ll also work without the http at all.

7) This does NOT work with the old object code embeds, only the iFrame embeds.

8) If you don’t have Google Analytics and the Tracking Code already on your site, this won’t work without it. It works WITH jquery and the GATC, but doesn’t include either.

9) We’re not currently tracking how long the video played. We had some errors with the API and wanted to make the value of the event the duration of watching the video on both the pause, and watched till end, but have left them off for now.

10) Sometimes there are problems and it doesn’t work for some people. We added some extra help, with a reloadFrames variable. It defaults to 0, but set it to 1, and it’ll rewrite the iframes with enablejsapi=1 and an origin of your current window.location.hostname, and then reload the iframes. If it’s not working on your site, try setting this to 1. It’ll cause the embeds to load twice, but they may track after that, if they didn’t before. For most people it should work without this setting.

DRAMATIC CONCLUSION!

Hopefully this makes some people’s lives easier. Tracking videos embedded on your site from YouTube is now a fire and forget solution. I know I was excited to get this to work, so I have to assume there are other people out there that will find this just as easy and useful.

Questions or comments? Post em below! If someone finds a problem with the code, let me know and you can help perfect it!

Sayf Sharif

About Sayf Sharif

Sayf Sharif is a Web Analyst, and expert in Usability and UX, who has worked with businesses large and small to maximize their online presence since the beginning of the Web, winning numerous awards along the way. Sayf has studied human tool use from the stone age (he went to graduate school for Archaeology) to the information age (he started programing on his father’s TRS-80), and is always interested in what goals people wish to accomplish using their tools, and how successful that experience was.

http://www.lunametrics.com/blog/2012/10/22/automatically-track-youtube-videos-events-google-analytics/

203 Responses to “Easily Track YouTube Videos with Google Analytics”

Tyler says:

That was the most entertaining script I’ve read…I guess ever.

Sayf Sharif Sayf Sharif says:

I’ll admit I was commenting it on Saturday from my couch and taking way too much pleasure in doing so. Glad you enjoyed it.

Nick says:

Due to various backend issue we chose to implement all our event tracking in a similar way; including recognising and categoriesing different groups of links on given pages. There are issues as you mention, but it’s worth considering as a viable option.

Sayf Sharif Sayf Sharif says:

Absolutely Nick. Adding in abilities so recognize groups and categories is a great addition to this type of code.

My main concern writing this was essentially thinking of a generic site with a generic blog, one where there were a myraid of videos embedded throughout the static pages, and then regular embedded videos posted on the blog, and how much a nightmare it would be to track them, particularly if the organization had a very tight IT funnel.

with code like this you could essentially add it through Google Tag Manager, if you had that working, and then without even bothering your IT department be suddenly and effectively tracking all your standard YouTube video embeds, with no other effort.

Ignatius Hsu says:

Sayf, nice code! This will save me so much time! I like the midieval-ish comments, too.

The way that the code captures the youtube id possibly needs to be broader that an exact string ?rel=0. See the available YT parameters https://developers.google.com/youtube/player_parameters#relstring .

What could be done is to use a regex in the js that pattern matches all characters after embed up until it finds a question mark. Something like: [^\?]+ would do the trick.

String matching is significantly more efficient than regex, but also more limited in use. So broadening from an exact strong match to a regex pattern would cause a small performance hit on the efficiency of your js. Something to consider to make this more awesome.

Sayf Sharif Sayf Sharif says:

It’s interesting you bring up the Regex because that’s what I first did to match the strings to grab the ID. I agree what I have is definitely limiting, and I think probably needs to be expanded in that direction, but when i first did the regex I was a little concerned that I was leaving things off, or getting an incorrect result. I don’t recall what it was, but I decided to just go explicit for the moment. Definitely a 2.0 version of this code I think should use a decent pattern match to grab more id’s.

I think I was worried about the order of the paramters in some cases? I think I need a bigger list of possible sources for the iframe with various parameters to make sure that we don’t lose anything.

But I think you’re absolutely right, adding the regex is a definite next step to take this a notch up.

Thanks for sharing that Sayif.

The code is quite entertaining. I have written the same thing in the past. You may want to take a look at mine, even though it’s not so well commented.

https://github.com/CardinalPath/gas/blob/master/src/plugins/youtube.js

I’m quite intrigued how you don’t need to use the enablejsapi=1 query parameter in the video url. The docs say it’s required. And in my previous tests it wouldn’t work otherwise. Maybe I need to re-run some tests as I have written this plugin quite a while back and had to rewrite some parts a couple months ago when the youtube api left beta status and did some breaking changes.

It probably needs some cleanup anyway.

Sayf Sharif Sayf Sharif says:

Eduardo, I originally didn’t add it and it worked. It looks like it throws a few security warnings in the Chrome console, but no errors, and it works, so I didn’t add it in.

I was curious as well that it was working, but if I could get it to work in my browsers without forcing a user to do anything but copy and paste their iframe embed code into their blog, I decided to try and get away with it. I’d be curious to hear how your tests go and whether it works with your code now as well.

Just tried it out and it works even without the enablejsapi=1. The errors in the console you mention happen regardless if you use it or not.

With enablejsapi: http://jsbin.com/ilutut/1/quiet
Without enablejsapi: http://jsbin.com/ilutut/2/quiet

You should take a look at mine because even though I use the enablejsapi I don’t require the user to add that. I change the iframe href on the fly. It seems hackish at first but it actually works very reliably. I even change object/embeds to iframes in order to track them with the api. So the user also doesn’t need to do anything special besides triggering my code at some point.

Thanks for sharing.

Recently I was working on some projects which also required tracking YouTube and Vimeo player events with Google Analytics. Because I couldn’t find any scripts or examples that met my requirements I wrote some scripts myself. You can find both scripts and documentation on GitHub:

https://github.com/sanderheilbron/google-analytics-youtube-video-tracking
https://github.com/sanderheilbron/google-analytics-vimeo-video-tracking

Sayf Sharif Sayf Sharif says:

Thanks for sharing your code Sander,

The main difference is that it looks like your code still requires some Javascript work, entering in YouTube ID’s manually, unless I’m reading it wrong. That seemed pretty standard across most of the code I found that worked. My issue was that I wanted to be able to instantly apply the code I had to the head of a website so that I could immediately start tracking possibly thousands of videos across thousands of pages with an upload of 2 lines of code rather than having to go page by page by page editing page specific scripts. (as long as the videos were embedded with the standard youtube iframe).

I was thinking of say someone who had a WordPress blog and wanted to be able to just embed a video on their blog (like our LunaTV’s!) where a Social Media person, or Marketing person with no knowledge and skill with javascript could just embed the copy/paste code on their site, and have it automatically tracked.

This is great! I’m going to use this on our site and all of our clients’ sites! More writing from Sayf would be great. I love that his writing style is the perfect mix of entertaining, intelligent, professional, and accessible.

Awesomeness!

Very useful script! Just gave it a testdrive and found two issues:
1. When using the scrollbar to skip parts of the video, it triggers many events for YT.PlayerState.PAUSED;
2. When videos are set to loop, the script will not trigger the event YT.PlayerState.ENDED, event when the video is watch till the end.

Hopefully this feedback can be incorpated into the current code.

Sayf Sharif Sayf Sharif says:

Martijn thanks for the feedback! I’ll look into both of those.

zxon says:

Nice script! Is there any way the title of the video could be used for the GA Event tracking label, instead of the video ID?

Sayf Sharif Sayf Sharif says:

Well unless I read the API wrong you can’t grab the title from YouTube.

https://developers.google.com/youtube/js_api_reference#Retrieving_video_information

You can get the URL, the embed code, and the duration. It’s not optimal. We talked about it internally here at LunaMetrics about what would be preferable. Just the YouTube ID? The URL stripped of parameter garbage? Ultimately we are using different things and modifying the code for different clients. One thing you find is that with all the parameters you can have the same video track as different ones, so you want to combine those as much as possible.

Optimally we could capture both the link as well as the title, I agree, but the only way we could see to easily get the title, was to have the user enter the title manually in the iframe included on the page, or in a separate html element named and included near it. That was not my intention for this, in that I wanted something people could track their videos on a fire and forget basis by simply embedding a youtube video on their blog and have it immediately be tracked.

But if we can come up with a better and easier way to grab that title and stick it into the event, I’m all ears!

yarvit0 says:

im trying to use the libs for my web but i get always a js error, at line 57 of lunametric-youtube.js: “Uncaught TypeError: Cannot call method ‘substr’ of undefined” any ideas?” it´s tracking anything T_T

Sayf Sharif Sayf Sharif says:

Yarvit, I’m seeing that actually here on the LunaMetrics website, but I’m NOT seeing it on my test server where i’m running a stripped down HTML page. I’m wondering if it’s some sort of conflict with other code on the page? I’m looking into it.

yarvit0 says:

im debuging and at line 50 of the libs:
var src = video.attr(‘src’);
it returns src as undefined;

Sayf Sharif Sayf Sharif says:

Yeah, i’ve tried changing how the jQuery is loaded, no conflicts, jQuery(‘iframe’) rather than $. Changed the variable to vidSrc rather than src. Added a basic check to only run those substrings if vidSrc has a value…

Still comes up as an Uncaught TypeError: Cannot call method ‘substr’ of undefined.

Sayf Sharif Sayf Sharif says:

It does bother me that i can’t reproduce the error on my test page though, there has to be some sort of conflict.

ThompsonPaul says:

Am just checking out the script, but in the course of looking at this page’s source I noticed an error indo9cating your caching plugin isn’t working correctly:

Just wanted to let ya know in case you hadn’t spotted it yet.

Paul

Sayf Sharif Sayf Sharif says:

Thanks Paul, but the weirdness there is that we don’t use WP Super Cache, it must be a relic comment from somewhere.

yarvit0 says:

i couldnt solve it, anyone have the same prob?

Diego says:

Hi!

Thanks for your script, we have adopted it and made some modifications to report as we do with another script.

One note, it would be nice that you checked that vidSrc exists in your script, considering the page can have some other iframes in it that do not have a src attribute (like in our case).

Like this:
if(vidSrc && vidSrc.length>29){

Thanks again!

yarvit0 says:

I have fix it
if (video[i] == undefined) {
var src = video.attr(‘src’);
} else {
var src = video[i].src;
}

its temporal, i think maybe its a problem with other iframes of the page (facebook and twitter logins)

Nicole says:

Thanks for this post – it came at exactly the right time when I needed it. Like some others, I made a few modifications which may or may not be helpful to others. To prevent conflict with other videos on a page (which may be tracked in a different way), I added a unique id attribute to each I wanted tracked – i.e. “track123456″ – and checked that “track” was there. Like others here, I wanted a bit friendlier title passed in the label parameter of the tracking so that it would be easier to recognize the video on the GA side. So, I added functionality which takes the title from the iframe and passes it as the label. (Less hands off than your approach, but most videos on my site are added to content dynamically based on a content structure with this metadata, so it works for me. And if it’s not, then this approach is friendly enough for content editors to be trained to do.) I also changed the URL matching to this regex, which works well (though I’m sure there are regex experts who could tell me a whole bunch of things wrong with it), saves some code duplication, and doesn’t care which URL parameters exist.

var regex = /https?\:\/\/www\.youtube\.com\/embed\/([\w-]{11})(?:\?.*)?/;
var matches = vidSrc.match(regex);

If it’s a proper YouTube embed URL, then matches[1] should hold the youtubeid.

Sayf Sharif Sayf Sharif says:

I’m going to apply these fixes and upload new code when i’ve tested it.

Does anyone have any thoughts on how best to solve the issue of it firing a slew of pause events when someone drags the player time scroll bar?

Riaan says:

I’m testing this on a wordpress site using the slidedeck plugin, and some of my slides are not showing after adding this script (Only ‘smart’ slidedecks are affected).

The strange thing is that the slides are defined using definition lists, no iframes anywhere.. I had a quick look and the source doesn’t seem to be any different… I can’t see anything in the script that could cause this, but my Javascript skills is rather rusty. I’ll do some more testing and see if I can figure out what’s happening.

Ileane says:

I’m using Viewbix to embed YouTube videos (works with Vimeo too) and the stats are great. I can also add an optin form for my email list. The videos get way more attention then the “vanilla” YouTube embeds.

Sayf Sharif Sayf Sharif says:

I haven’t seen any specific data on levels of engagement with Viewbix videos, but there are other free ways to add interactive links to videos. In general though I’m not a fan of splitting analytics. I want to see in one spot, in this case Google Analytics, what is happening on the site, and how that relates to conversions. Do video viewers convert more on my site? I want to see that inside Google Analytics, etc.

Sayf Sharif Sayf Sharif says:

Riann, I’ve seen general problems with smart slidedecks and youtube videos in general. Which slidedeck are you using? My immediate though is that the jquery is messing things up with how the jquery for the slidedeck is setup, and how the code in this is.

Riaan says:

I’m using one called slidedeck (slidedeck.com). You can see the site on coretalk.co. For now, I’ve disabled your script, but the problem is with the slides in the sidebar (Yeah, I know – Slides in a sidebar!? – Shareholders will be shareholders though)

Sayf Sharif Sayf Sharif says:

Yeah, obviously slidedeck uses jquery and some complicated javascript to function, and it doesn’t like something in this one. My money is still on some sort of jquery conflict.

Chris Green says:

For the paused event… Wouldn’t this work?
For your review…

var _pauseFlag = false;

….

function onPlayerStateChange(event) {
//it tries to trick us with a number one greater than
//that of our arrays. But we outsmart it.
//with math.

videoarraynum = event.target.id – 1;
//Should the video rear it’s head
if (event.data ==YT.PlayerState.PLAYING){
_gaq.push([‘_trackEvent’, ‘Videos’, ‘Play’, videoArray[videoarraynum] ]);
_pauseFlag = false;
}
//should the video tire out and cease
if (event.data ==YT.PlayerState.ENDED){
_gaq.push([‘_trackEvent’, ‘Videos’, ‘Watch to End’, videoArray[videoarraynum] ]);
}
//and should we tell it to halt, cease, heal.
if (event.data ==YT.PlayerState.PAUSED && _pauseFlag == false;){
_gaq.push([‘_trackEvent’, ‘Videos’, ‘Pause’, videoArray[videoarraynum] ]);
_pauseFlag = true;
}
//and should the monster think, before it doth play
//after we command it to move
if (event.data ==YT.PlayerState.BUFFERING){
_gaq.push([‘_trackEvent’, ‘Videos’, ‘Buffering’, videoArray[videoarraynum] ]);
}
//and should it cue
//for why not track this as well.
if (event.data ==YT.PlayerState.CUED){
_gaq.push([‘_trackEvent’, ‘Videos’, ‘Cueing’, videoArray[videoarraynum] ]);
}
}

Ken says:

This is exactly what I’ve been looking for but it’s not working. No errors, just doesn’t work.

Ken says:

I’m not sure but it’s possible YouTube changed something on their end. The onYouTubeIframeAPIReady function is fired before your trackYouTube function, causing the videoArray.length to be 0, therefore nothing happens.

Trevor says:

This is definitely not working, and I’ve spent hours checking and double-checking my process. Definitely a bummer, I should have read the last two comments before devoting so much time to troubleshooting!

Google Analytics reads “4 of your visits sent events” but there is zero information regarding those events.

Sayf Sharif Sayf Sharif says:

Ken and Trevor,

I’m not sure exactly which issues you’re having. The code as described above is working on this very site, and on our test site. Are there no other errors getting kicked up? Are the embedded videos flagged as secure or to not be tracked? Were the embedded straight from the YoutTube site? If you read this, can you email me at sharif@lunametrics.com with an example page that doesn’t work for you, including the code implementation, and the embedded iframe? I’d like to take a look to see if i can reproduce and fix the problem.

I had a little issue that some others may run into, and the fix is easy. I was getting an undefined error related to mySrc, and tracked it down to the fact that Olark generates an iframe without a src attribute which kills the rest of the loop, apparently.

I fixed it by changing this:

if(vidSrc.length>29){

to this:

if(vidSrc && vidSrc.length>29){

So basically now it checks to make sure vidSrc actually exists and then looks at the length. The length check wasn’t enough because it would throw the undefined error on the length check if vidSrc was undefined.

AJ says:

Hi Sayf,

Great giveaway, it solves a big problem for a lot of people.

I have a question, does this code work only with asynchronous GA tracking code or it also works with the traditional GA code?

Al says:

Awesome. We always ran into issues getting accurate tracking of embedded videos on client sites. Uploaded your JS and bam! instantly tracking correctly.

Thanks

Sayf Sharif Sayf Sharif says:

AJ,

My guess is that it might not work with the traditional code, but I haven’t tested it.

Robert says:

Hi Sayf,

This is a recommended approach for evaluating youtube urls and the youtube video id with regex:

URL MATCHING:
if(url.match(‘http://(www.)?youtube|youtu\.be’)) { do.something() }

GET THE ID:
var youtubeId = url.split(/v\/|v=|youtu\.be\//)[1].split(/[?&]/)[0];

Viktor says:

Thanks for the code! It is very useful for embedded video tracking. The install takes only 2 minutes and it works!
Awesome idea! :)

AJ says:

Thanks Sayf, too bad it doesn’t work with the traditional code. I have to do the OLD School setup again for a site, how come there is no simpler way for Traditional code.

jim says:

FYI, I think you need to add the http: before the link in your code above:

Sam says:

Great script but do you know why it doesn’t seem to work in IE? I am seeing almost no plays, pauses etc from IE users despite them making up around 30% of visits. Also I am testing a bit in IE8 and can only get events to fire intermittently. Anyone else having problems in IE?

Mihnea says:

Hi Sayf!

It seems a pretty simple tutorial. However, I didn’t manage to track the events in GA. I have added the scripts as you said. Also I have added the Youtube video with the recommended iframe code.

I got nothing. I use Asynchronous code for GA tracking. Does anyone encounter this problem with the code above?

Thanks

Art says:

@Mihnea

I have the same issue. I insert the code right after my GATC and before the , with no results.

I’m a marketer by trade, know very little about coding and don’t have budget to hire anyone. That said, any suggestions for getting this tracking code to work via my Tumblr blog would be greatly appreciated

jim says:

This code did not work for me either. Nothing appears in GA.

Sayf Sharif Sayf Sharif says:

I’ve updated the code to version 2, with some of the above suggestions. It now uses regex to match the iframes, so should match more odd youtube iframe embeds better. In addition I added the pause script suggestion to prevent the rapid fire pause events that would occur on dragging the slide bar. I’ve tested the code and it’s working.

If you are having problems getting the code to fire, confirm that your jquery and youtube code is placed on the page after the main Google Analytics Tracking Code script, and that you’re linking to them correctly. Download the javascript, and host it on your own system, rather than linking to the code on lunametrics.

However the script does seem to have some Internet Explorer cross browser compatibility issues, and does not fire consistently on IE. Will look at that for the next version of the code.

Micah says:

Awesome! Although I didn’t use it as just an include, since I had to integrate it into a custom analytics tracking module. But I used all the ideas in it. Life saver! Thanks :D

Tathiana says:

Great script, except I can’t get it to work on my site. In the Chrome console an error is shown: Uncaught TypeError: Cannot call method ‘match’ of undefined. Any ideas, or anyone else having the same problem?

Sayf Sharif Sayf Sharif says:

Tathiana,

Thanks! While it worked on my test site, I was able to reproduce your error. I added in some slightly better logic to catch that occurrance, and if you download the script again and use the new script included, there is additional logic on line 65 to catch that error, and it should now work.

David Hughes says:

Thanks for the script. We were having some issues with it not reporting on IE and I found a fix that seems to work.

I added two new booleans, documentReady and youtubeAPIReady which are set to true when those respective methods are called.

Then, I added a new method which is called by an interval:
function initialisePlayers() {
if (youtubeAPIReady && documentReady) {
clearInterval(initPlayersInterval);
trackYouTube();
constructPlayers();
}
}

I’m not sure how good of a practice this is, so any advice would be great & I hope this helps someone :)

Sayf Sharif Sayf Sharif says:

Interesting, I’ll have to take a look at that.

Chris says:

Thanks for this! I am not familiar with javascript, so I am dependent upon great work such as this. I am, however, having some trouble. I am hoping someone can help me.

I am running a blogger site. Each post is a video. The main page shows the 5 most recent posts, and therefore contains 5 videos. Videos are placed on the page using the standard embed method.

In the header of the site template, I have added the following lines:

So here is what I get:

1. First video on the page does generate alerts.
2. Other videos on the page do not generate any alerts
3. Only two alerts are reporting: PLAY and PAUSE

What am I missing here? Thanks for the help!

Chris says:

Sorry… the comment engine must have stripped the script code. It’s essentially the two lines that are posted in the blog entry here after the words: ” So you’ll have something like this…”

I have one line for jquery and one line for lunametrics-youtube.js.

Sayf Sharif Sayf Sharif says:

Ok I see it now on your site. This isn’t the first time I’ve seen this error with the code, but i can’t reproduce it on my test server, which makes me wonder exactly what is causing it.

It often seems to revolve around the matching aspect and your site is kicking up the error:

Uncaught TypeError: Cannot call method ‘match’ of undefined

Maybe that’s not being handled right in the code. I’ll see what I can do to change it, unless anyone else listening in on this thread has any suggestions.

Chris says:

Thanks! I did see your visit, and it looks like the first video on each of the two build pages registered the play/pause events.

I appreciate you looking into this. Your script was exactly what I needed. I’ve tried the former methods, and they don’t work with multiple videos on the same page. I was very excited to find this. Now if we can figure out what the deal is, maybe it will help others too.

For everyone else…My site is a Blogger site, so it may be unique to their formatting?? Not sure. I know only enough to get into trouble. : )

Chris says:

I’m way out of my comfort zone here, but I did try copy/paste the actual code into the page, rather than using your externally host js. It didn’t work either, but maybe there is some intel that will make sense to you. When I tried to use the code direct, It wouldn’t accept some of the formatting.

I had to change && references to &amp:& and I had to change < to $lt;

This could just be a product of the Blogger html editor (since it checks for malformed tags or code issues before letting me save) and may not really have any impact to the actual code processing in real-time, but I figured I would bring it up in case it was of some value to you as you try and troubleshoot.

Chris says:

Sorry….It should read:

I had to change && references to && and I had to change < to <

(I can't edit the comment)

Sayf Sharif Sayf Sharif says:

One thing that you could do would be to try hard coding the video. It goes against my goal of this code which was to just have a script to have on all your pages to automatically track all videos across a site without hand coding anything….

However this has worked in the past when for whatever reason the client server has a problem with the match method. To change the trackYouTube function in the above code to the following, and then just have if/else statements to match the actual src value for each video on the page, and bypass a regular expression match entirely. It’s definitely a kludge, but if you only have a few videos, it could work and solve the problem.

Look at the iframe on the page, and then take the value of the src which would be something like “http://www.youtube.com/embed/YOUTUBEIDHERE” where YOUTUBEIDHERE is the YouTubeId like.. PtHET657o and copy that whole value into the line below with it, and then just the youtube id into the two spots for it there. Then even just copy and paste that entire if statement again and do it for a second video.

Essentially you’d be manually building the array of videos on the site rather than comparing them via a regex match. This wouldn’t even really be the best way to do this staticlly, because you could do it without even running through every iframe on the page to check, you could just set them, but this seemed like it might be easier to understand for a non developer.

let’s hope my code pastes below…

function trackYouTube()
{
var i = 0;
$(‘iframe’).each(function() {
var video = $(this);
var vidSrc = “”;
vidSrc = video.attr(‘src’);
if (vidSrc == “http://www.youtube.com/embed/YOUTUBEIDHERE”){
videoArray[i] = “YOUTUBEIDHERE”;
$(this).attr(‘id’, “YOUTUBEIDHERE”);
i++;
}
});
}

Chris says:

I appreciate the time Sayf. This option does not work in my situation, since I cannot predict which videos will be displayed at any given time They are dynamically presented based on recency, search results, etc.

Would it be possible to remove the trackYouTube function all together and just hard code adding the video ID into the array down in the page where the video actually is? I don’t know if this is possible, but I was thinking of something like this (pseudocode):

Add the code that can be loaded up front that doesn’t require the array to be in place yet.

Have the various videos iframes and an associated javascript code that simply adds the video ID to the array. No regex, no if/then, just a statement that loads with the video pages.

At the very end, after all of the array statements have been made, can I then load up the rest of the script that is dependent upon the array being complete. I imagine this is the meat of the code, loading at the end rather than up front. I realize their is some risk of activity before the script fully loads, but I see it as minimal.

Does any of this make sense, and is it possible to reconfigure in that fashion? I realize it takes away from the fire-and-forget intention of the script, but it doesn’t seem like that is going to work in my situation. I’d rather not go manually add javascript code to my 50ish videos, but will do it if that’s the way to get it to work. Future videos are not a problem, since I can just make it a part of the template for new posts.

Thanks!

Chris says:

Dang…looks like my “free support” expired. : ) LOL. Just kidding. I may have to look at plan B…I just can’t get this to work. Unfortunately, all of the articles and blogs that start to address the situation eventually point to this blog. Kudos for solving most everyone’s needs! Stinks, tho, that there are a few of us that this doesn’t work for. I even tried looking for conflicting javascript, but didn’t find it. I’ll continue to check back from time to time, just to see if anyone comes up with anything. Thanks!

Sayf Sharif Sayf Sharif says:

Chris, et al,

i’ve just updated it to 2.1. I’ve added the following code.

if(video.attr(‘src’)===undefined){
var vidSrc = “”;
}else{
var vidSrc = video.attr(‘src’);
}

Before I wasn’t checking for something being undefined and it would error out on the match portion later, but only on some servers. I don’t know WHY it only happened on some servers and not others, but there it is. This check though seems to fix that Type match problem, and might solve some of the problems for people where the code would not fire.

Chris says:

Sayf,

Thank you, thank you, thank you!

This seems to be working great now on my site. I really appreciate it, and glad you were able to figure out the issue. I am glad I decided to check back today!

Sayf Sharif Sayf Sharif says:

Chris, Glad that fix worked for you!

Thanks for posting this, Sayf! I was about to code the same thing, so you saved me the time. Much appreciated! I wanted to include the video time at the time of the events, so I’m glad you mentioned getting some errors in the API.

The comments in the code are amusing. =) Do you have a clean, minified version of the code available for download?

Sayf Sharif Sayf Sharif says:

I don’t have a clean version available (though I’ve made one in the past by just hand deleting the comments). I should really just get my act together and put a version in github or something to make it easier for people to play with.

good idea for the video time in the events. I just did that with my jplayer tracker, and it’s really useful for analysis.

Luca says:

This just doesn’t work for me and I don’t understand why. On the JavaScript console on Google Chrome, I get this:
——-
Uncaught SyntaxError: Unexpected end of input js_lunametrics_youtube2:184
Unsafe JavaScript attempt to access frame with URL http://www.mysite.com/ from frame with URL http://www.youtube.com/embed/___________. Domains, protocols and ports must match.

Sayf Sharif Sayf Sharif says:

Luca, the unsafe javascript attempt is normal, that’s not the problem. The Uncaught SyntaxError is. I’m not sure what’s cuasing that, it looks like maybe you renamed the file? Or edited it? It’s hard to say without looking directly at the page with the error. Something looks like it wasn’t properly implemented though.

James Wann says:

Hi there,

Just wanted to mention a product that already does embed analytic measurement with depth tracking:

vidanalytic.com/

We also do Dailymotion and Vimeo for multi-source curators.

(P.S. I work for vidanalytic – so this is a shameless plug :-P )

Sayf Sharif Sayf Sharif says:

James, I’ll allow the plug. :) However, I have to say that while your service appears to give good information on video usage, it is completely disconnected from the remainder of your site tracking.

My goal with the various video tracking scripts I’ve posted is to bring in that video data to Google Analytics, where you can see where a user came from (say a campaign tagged email marketing blast) and what they did on the site (like watched a couple of videos, viewed a few product pages), and then ultimately whether they converted on your KPI (they purchased a product, they filled out a contact form, etc).

Bringing that data into Google Analytics let’s us compare how the videos perform in relation to where the visitors came from, what they also looked at on the site, and how they ultimately converted. If there are 20 videos on a website, just knowing that all 20 were viewed 10 times each doesn’t help increase our conversion by itself. Knowing however that viewing Video A led to 50% of the viewers to purchase a product, versus Video B which led to 0 purchases can let the company say “hey maybe we should use more of Video A” etc.

James Wann says:

Sayf, that feature is definitely on our roadmap (and accelerated based on your feedback!) – we’ve seen some adoption over the past few months from folks hosting A/B test pages – and something like that will definitely take it to another level. :-)

Leone says:

Hello Sayf,
I love your script! Yesterday I succeded in getting it working, I left the office got back and it wasn’t working anymore! that sound weird I know.
Looking at the console I get:
chrome.windows is not available: You do not have permission to access this API. Ensure that the required permission or manifest property is included in your manifest.json. binding:202
Uncaught TypeError: Cannot call method ‘getCurrent’ of undefined popup.js:17

Not sure this could cause it.
here you find my attempt:

Leone says:

I even tried to create a new blank page (which is correctly tracked by realtime on Analytics) with your code I found on the source of this page and this video:

NOthing: I see someone is browsing that page but no events is showing in realtime–>events
The weird thing is that it was working yesterday evening!

Viktor says:

Hello,
I can’t seem to get your script to work if the videos have been embedded using the javascript method (where the iframe is created dynamically), instead of writing iframe tags into the actual html source.

That is, using this method: https://developers.google.com/youtube/iframe_api_reference#Loading_a_Video_Player

I can successfully attach events for onReady and onStateChange like in the example, but I would like to use your script (or a similar one) in order to not have to specify events manually for each video I embed.

When I include your script as-is, it stops the video frame from loading completely. I suspect it could be due to it creating new YT.player objects that replace the original one.

Chris S says:

Sayf,

For the most part this seems great, and I’m really excited about this, but it seems that there is a strange anomaly happening with the reporting. The code has been running for two days, and over the past two days google Analytics is reporting that 18 of our 99 videos that have been viewed thus far have 1 transaction associated with them, with the Revenue being $277.83 exactly. I looked into our order management system and there has only been one order equaling $277.83. So could there be an issue here, or is it possible that an individual watched 18 different videos before making a purchase and each video is being tied to that purchase?

Thanks,
Chris

Jo says:

Hey, I tried your method & downloaded the file & followed the direction.
But still not firing off. My website is a wordpress, I get this message when I was debugging it via chrome.

Blocked a frame with origin “http://www.youtube.com” from accessing a frame with origin “http://mywebsite.com”. Protocols, domains, and ports must match.

Have you seen this before?

Sayf Sharif Sayf Sharif says:

Leone, I’m not sure. Maybe the latest version of jQuery has an issue with something? i just checked here on LunaMetrics and it’s still working. Try putting an id on each iframe, and also moving the code up higher on the page above the GATC and see if that fixes it.

Sayf Sharif Sayf Sharif says:

Viktor, yes this isn’t compatible with dynamically generating them. You’d have to modify the script so that our video script wouldn’t actually run until after the videos are all loaded onto the page, and then it probably would work.

Sayf Sharif Sayf Sharif says:

Chris you should be able to tell how many people watched the videos. One way you might want to look at is to segment video viewing sessions from sessions that didn’t view videos. Create an Advanced Segment which includes video event category, and another segment which excludes them. Then you can compare visits where people viewed videos and those that didn’t.

however if you’re seeing 18 videos viewed, and they all have that same transaction amount, and no other videos do, my feeling is that it’s one person who watched them all and purchased.

Also keep in mind, I think it says this somewhere, that this still is iffy or doesn’t work at all on IE, so if you have a large volume of IE traffic, it’s not going to capture everything.

Sayf Sharif Sayf Sharif says:

Jo that error message is normal with the script, and shouldn’t stop it from working. There must be some other problem with how you implemented the code. If you want, you could create a test page and put the URL in here as the website URL on your comment and I’ll take a quick look.

Jo says:

here’s the test page that you can look at.
http://westernshootingjournal.com/about/test/

Sayf Sharif Sayf Sharif says:

Jo,

Not sure if that’s the right page. There was no youtube video on that page, and the youtube script itself looks like it wasn’t loaded/pointed to on the page properly.

Jo says:

I had to take it down for the wkend.
please look it the page now.

Sayf Sharif Sayf Sharif says:

Is this the script to include our code?

<script type=”text/javascript” src=”http://westernshootingjournal.com/wp-content/themes/atahualpa/js/utube.js”>

If so, then the quotes are ‘curly quotes’ and need to be changed to regular quotes like “. In addition that file doesn’t appear to exist on the server. It looks like the filename was cut off. so it’s definitely not loading our script correctly that I can see.

Jo says:

yes that is the script.
double checking the file it does show up on my browser, I did rename the file to utube.js

Sayf Sharif Sayf Sharif says:

It’s definitely not loading for me in Chrome. Watching the Console while the page loads I get this error:

Failed to load resource: the server responded with a status of 404 (Not Found) http://westernshootingjournal.com/about/test/%E2%80%9Dhttp:/westernshootingjournal.com/wp-content/themes/atahualpa/js/utube.js%E2%80%9D

Jo says:

Yes, when I run the debug in chrome, I do see this. I double checked the server, and the file is there. so I’m confuse at the moment.

Mark says:

I am getting a console error when any event is trying be fired, such as pressing play:

“Uncaught ReferenceError: _gaq is not defined”

Mark says:

If it makes any difference, the tracking code GA gave me today doesnt look like the codes I’ve used in the past (details hidden):

(function(i,s,o,g,r,a,m){i[‘GoogleAnalyticsObject’]=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,’script’,’//www.google-analytics.com/analytics.js’,’ga’);

ga(‘create’, ‘UA-########-1′, ‘domain.co.uk’);
ga(‘send’, ‘pageview’);

Shahar says:

Mark,
Like it says in the notes, you need to be using the older ga.js code for this to work, you can find it here:
https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingOverview

Sayf Sharif Sayf Sharif says:

Mark and Shahar,
Yes this works with the asynchronous tracking, not the new universal tracking which is still in beta.

Matt says:

Thanks for sharing this. Does the script account for HTML5 YouTube videos? HTML5 is typically used by YouTube when a user does not have flash installed (iPads).

Matt says:

By the way, it may be helpful to add the part about it not working with universal analytics to the notes part at the bottom of this post. I would not have realized it if I didn’t notice it in the comments section. Thanks again.

Oleg says:

Hi Sayf,
thank you for the amazing script. You saved a lot of my time.
I have a little question. Are there some restrictions on the jQuery version?

Oleg says:

Also during the test of Your remarkable script I found that it doesn’t track events when I use Opera browser.
It shouldn’t work in opera or it’s my own problems?

Igor says:

Thank you very much for sharing script. I am trying to implement the script but do not appear on the Analytics events…

The web page is http://www.palaciodeesquileo.es/bodas-segovia.html

What I am doing wrong??? Thank you in advanced an than for sharing

Sayf Sharif Sayf Sharif says:

Matt, yes it tracks on both iphone and ipad.

Sayf Sharif Sayf Sharif says:

Oleg, I never tested it on Opera so I’m not sure. It does have problems sometimes with IE versions prior to 10, but IE 10 it does work. So it’s possible Opera had the same issues.

Sayf Sharif Sayf Sharif says:

Igor, Sorry that page won’t load for me at all. If it’s not working, remember to have the jquery and youtube script in the head of the document, rather than in the body, and be sure that the files are actually being pointed to correctly and loading onto the page. Those seem to be the most common problems people have.

Oleg says:

Sayf, thank You for the answer about Opera.
Please, answer to my question about jquery version. Is there any limits for jquery version?

Sayf Sharif Sayf Sharif says:

It works with the version I listed above, but I haven’t tested it for earlier versions.

gal says:

Hallo sayf,

I have a qusestion about the code position,
I have 1 page Website which were made in wordpress,
my question is where I need to put the code in wordpress case, where I can’t specify a page?
should I put it in the header.php?
(I am using the “page builder” widget to push video to my page).
Thank you very much for your time and Patience!

João Victor says:

Hi Sayf,
Thank you very much! Your script saved my life!
But I would like to know how can I get the elapsed time of video that user already watched. Is it possible? How can I achieve that?
One more time, thank you!

Sayf Sharif Sayf Sharif says:

gal, yes put it in the header.php file in that case and it will be included on every page, which in your case is just that one.

Sayf Sharif Sayf Sharif says:

João Victor, you’re welcome. Other players have a bit of an easier time with elapsed time, see my jplayer or video.js posts. Those it’s easy to do some sort of TimeWatched trigger. Youtube however does not send that information with the api (at least it didn’t last time I checked, but I’d love to be wrong and corrected on that).

So that leaves you with doing something like manually tagging the videos with a length in some way, and then setting an interval on the page in javascript and occasionally at certain points sending that time elapsed data, tied into the play event occuring, but no stop or pause event, etc. Keep in mind that you’d still be limited to 500 events per session, so you won’t want to be sending that every second, etc.

In general if you want more accurate second by second tracking, you might want to examine using another player.

gal says:

Hi Sayf,
first of all thank you very much for the quick respond.

unfortunately it doesn’t work )=
I added to your code YourCode,(otherwise I just see it on the Website) in the header.php(between the tag),
my youtube tag look like this:

I am sure that I’m doing something real stupid and I hope you could track it out (=.

Again, thank you very much!!

gal says:

…”I added to your code” script “YourCode” /script tags…

gal says:

SORRY! I am not use to write in forums XD.
“my youtube tag/code look like this:”

iframe class=”alignnone” width=”100%” height=”450px” src=”http://www.youtube.com/embed…” frameborder=”0″ allowfullscreen /iframe

Sayf Sharif Sayf Sharif says:

gal, do you have a link to the page that I can see? If you include the 2 lines of code to be sure to include jquery, and the video code within the head section of the html, and are using asynchronous ga, not the universal analytics code, and have the iframe on the page, it should work. Maybe one of the files isn’t pointing correctly and not loading?

gal says:

Again thank you very much for your respond.

I’m working on a project from the company, thats why I preffer not to post it here.

if i understand correctly (please correct me if I’m wrong),
when user get into my Website, click on the youtube video (of course the video is in the landing page), and immediately get out, the GA recognize this user as bounce, and that’s exactly what I’m trying to prevent.
That’s why your code is perfect for me, your code track every single click on the video and does not count the user in as bouncerate.

1)I put the code in the header.php – in the head tag between “script” tags,
2)in “TEXT AREA” widget (from “pagebuilder”)I put the iframe with the youtube video in it.
3)Under the iframe write the 2 jquery codes from above.

And still, when I test the connection, by entering the site, click “play” and leave it’s still count me as a bouncer.

Maby I am 100% wrong with the purpose of the code.

Again TY very much for your Patience (if left any XD).

Sayf Sharif Sayf Sharif says:

Well the lines of code need to be in the head section of the page, but you don’t need to add extra script tags around them so:

<script src=”//code.jquery.com/jquery-1.7.2.js”></script>

<script type=”text/javascript” src=”http://www.lunametrics.com/js/lunametrics-youtube.js”></script>

</head>

You could copy those scripts into that location just like that. Make sure you’re not copying in jquery twice, see if it’s on the page already (it probably already is there on the page).

Then include that iframe so it shows up on the page in the body content somewhere. I don’t know what the text area widget is, but if it puts that code in an actual HTML Text Area, then that won’t work. If your’e seeing the video on the page, and can play it though, I’m assuming it’s just the wordpress text widget, and that should work fine. However you don’t need to include jquery after the iframe. just put those scripts into the head section within the header.php file, then icnlude your iframe, and it should work.

Sayf Sharif Sayf Sharif says:

Also watch for curly quotes in the script lines if you copy them, sometimes they seem to get added by wordpress and it’ll break the code and it won’t actually get included.

tlock says:

Your code works great. Thank you for sharing. I’m wondering how I can be more specific with the naming of each video. With multiple pages and videos the analytics starts to get difficult to decipher with the conventional YouTube naming structure. It would be great if I could assign more specific names to each.
I appreciate any assistance you can offer.

gopal says:

yes put it in the header.php file in that case and it will be included on every page, which in your case is just that one.for more:http://www.publiclikes.com/youtube_like.php

gal says:

Hi Sayf,
Happy fourth of july!
I did my homework and I managed to implement your code in to my Website,
I must say it works PERFECT!
The problem was, I used a GA traditional code and not Asynchronous.
I would like to thank you very much for your support and patience!
again thank you and wish you all the best!

Sayf Sharif Sayf Sharif says:

tlock, you can use the YouTube API and retrieve the title of the video I believe. When the first implemented this we were using the title of the video but for some reason we changed it to the YouTube ID I don’t recall why but something tells me there was some reason why the title might change where is the ID did not. However I agree it is unwieldy at times and hard to recognize buy eye.

Sayf Sharif Sayf Sharif says:

gal, glad it worked for you!

gal says:

Sayf one more question (=
is there any way that I could see in the GA
content> event> overview > label event, the name of the video and not the ID?

TY

Sayf Sharif Sayf Sharif says:

gal, here is a link to some people that built off this code in a way that Incorporated the title of the video as opposed to the ID: http://www.isitedesign.com/labs/2013/01/21/how-to-track-youtube-video-playback-with-jquery-and-google-analytics-events/

check it out it might answer your question.

gal says:

PERFECT!

thank you very much!

Andy says:

This script is very useful. THANK YOU.

You can get individual video progress by using setInterval to call a function every 1/2 second or so that loops through the playerArray and uses playerArray[i].getDuration() and playerArray[i].getCurrentTime() to calculate the amount of video that has been watched. Once that crosses 25% (50, 75%), fire a Google tracking call.

Matt says:

Thanks again for this script and responding to so many comments. Looking at the JavaScript, it looks like modifying this to work with Universal Analytics would only require changing all of the lines with _gaq.push to the new analytics.js events format. Is that a correct assumption or am I overlooking something?

Sayf Sharif Sayf Sharif says:

No that would be all you would need to do I think.

Jim says:

Hello,

This is an awesome script. Nice code commenting too!

Is there a version of this that would work with the Universal analytics.js? We are already using analytics.js to track other events across our site.

I tried to modify the script a bit to work with Universal Analytics per this post http://stackoverflow.com/questions/16015364/how-to-set-up-ajax-call-tracking-in-google-analytics to no avail.

Sayf Sharif Sayf Sharif says:

I’ve added a second version above that should work with Universal Analytics.

Kirsty says:

Laughed heartily at the commentary throughout the code. A ray of light in my work day!

Sayf Sharif Sayf Sharif says:

Glad you enjoyed it Kirsty.

Todd says:

This is great. Do you happen to know how to adjust the script to work with embedded vimeo content?

vinot says:

Hi,
I tried to implement your code.. but its not working for me… Plz have a look at it and help me.
http://vinonsearch.com/youtubeaug3.html

Thanks in advance..!

Sayf Sharif Sayf Sharif says:

Todd, try the code at http://sanderheilbron.github.io/vimeo.ga.js/ he beat me to a good vimeo implementation.

Sayf Sharif Sayf Sharif says:

vinot, your youtube embed needs an http: at the start of the source address, noit just a //

Vinoth says:

Thanks a lot sayf. Its working now.

Tried to use the same code in GTM. Its not working.
Whether I need to change anything in the code for Implementing Youtube video tracking using GTM?

Sayf Sharif Sayf Sharif says:

I haven’t done it through GTM, but I guess it could mess it up depending on the order it loaded in, though.

James says:

Hi, the script doesn’t work with the current youtube embed code as the regex doesn’t match `//www.youtube.com…` when missing the http.

I’ve fixed it in my own version by using `(https?\:)?` and using the third capture group maches[2] instead of matches[1].

You can see my changes and updated script at https://gist.github.com/Jamedjo/6184913

Lauren says:

This is exactly what I have been looking for. Thanks so much!

My question is where on the page does this script get placed so it can be used? And if the script doesn’t go on the page that I have the video embedded in where does it need to get put?t doesn’t go on a script where do I need to put it

Roie says:

Works perfectly, though cannot seem to find a solution for wmode transparent, any suggestions?

Sayf Sharif Sayf Sharif says:

James, thanks youre right. I wonder when they changed that. Very annoying.

Sayf Sharif Sayf Sharif says:

Lauren, in the head section of the HTML preferably. It needs to be included on the page delivered in some manner for it to work. In theory I think you could probably put it anywhere on the page.

Sayf Sharif Sayf Sharif says:

Roie, I haven’t looked at that at all, sorry. Maybe another reader has a solution?

George says:

I was very excited to find this solution, only to find that it doesn’t work for me.
I tried on different domains, with the new universal analytics code (ga) and the classical one (_gaq). Neither worked!
Could you please take a look at zeebeauty.net and tell me if you see anything wrong on missing?
Thanks!

Sayf Sharif Sayf Sharif says:

George your double quotes in the script got turned into curly quotes.

Edit the jquery line and the youtube code line and replace the curly quotes with plain double quotes. You can do this easily by converting it to plain text as well in a text editor.

George says:

Thanks for that, but replacing the curly quotes with plain double quotes didn’t fix the issue.

George says:

Hi Sayf,

I have even tried with the classic (gaq) GA code and your corresponding js.. but unfortunately the result is the same – NO events recorded in Google Analytics.
So, at this point, zeebeauty.net contains the new code and zeebeauty.com contains the old code. Neither works.
Do you have any solution or any other suggestions?
Thanks in advance!

Sayf Sharif Sayf Sharif says:

George,

the code is loading correctly now, but the problem is that Youtube very recently changed the default source for it’s embeds. try going to the iframe embed code and before the //www.youtube.com add the http: line so that the source of the iframe starts http:// instead of just // and it should work. I need to update these scripts to compensate for their change.

George says:

unfortunately the adding of “http:” didn’t fix the problem. Events are still not recorded.
Any other possibilities?

Lope Titi says:

HI Sayf,

I was able to get this working but the event fires at random (It fires on some youtube videos and not other).

Has anyone experience this?

Could it be that the YouTube IFrame Player API loads before document.ready, which fires the YouTubeIframeAPIReady, while the videoArray is still empty.

Any idea to resolve this will be highly appreciated.

Thanks.

Lope.

Tiffany says:

We added the “http:” in iframe for youtube URL and we are now able to see some tracking, but like Lope, we not seeing tracking for all 12 videos on the page.

We also continue to see the following error:
Blocked a frame with origin “http://www.youtube.com” from accessing a frame with origin “http://www.website.com”. Protocols, domains, and ports must match.

Any help is appreciated!

Lope Titi says:

Further to the message i sent above, I think the problem is with jquery not aware of the page object when the object changes via Ajax with the page is not reloading. I have my Youtube videos in a carrousel and i noticed that the event tracking is fired only for the first video and not for the others on the page.

@ Tiffany — is this the same situation as yours and have got any work around on this.

Lope.

Tiffany says:

We originally had 12 videos on one page, but just recently updated one page per video. Tracking is still not working.

Please take a look at this page and offer any suggestions. meditab.com/client/client-voice/allergy-ehr-improves-practice-efficiency/

We really need to get this tracking issue resolved. Thanks in advance!

George says:

Automagically, one of my websites onto which I installed the lunametrics js started recording events – it uses the classic GA code (gaq_).
Another website with the new universal analytics still won’t record events.

Sayf Sharif Sayf Sharif says:

I’ve updated the code. It should work with the current newer form of YouTube embeds as well as older ones. try updating to the code now included above as the download code (marked version 5) and it should track right out of the box.

Sayf Sharif Sayf Sharif says:

Per some of the above questions. The code runs when jquery is loaded, and ready to go. It then scans the page for youtube iframes, and runs it’s code accordingly when it finds youtube events.

If you dynamically add the embed code later through ajax, it will not get tracked straight out of the box, because the code isn’t seeing that iframe when the page loads. To do so you would need to turn our code into a function that both runs on load, as well as again separately when you dynamically load additional video embeds.

avejidah says:

Thanks for the example. *Small gripe a bit about the comments.*

Looks like Lope pointed this out already, but there seems to be a race condition there. If onYouTubeIframeAPIReady fires before document.ready, the videoArray will be empty. It works _most_ of the time, which seems worse than not working since it’s hard to tell that it’s not working!

This is an easy fix with a $.Deferred. When the API is ready, queue the YT.player creation against the $.Deferred. Then resolve the deferred when trackYoutube() completes. Three-line fix!

Anyway, thanks again for the snippet. /me goes and brushes up on ye ol’ C.

Sayf Sharif Sayf Sharif says:

That’s an interesting idea, I will look at adding that in the next version.

Dave says:

Hello,
I seem to be having a problem with ‘Total Events’ and ‘Unique Events’.

For example I have 1 video on a page. In google analytics that page got 10 unique views. However in the events analytics for that page it says it got 120 total events and 8 unique events. Why is the total events so much higher? There is no way that out of 8 unique people that they paused/play or it went from buffering to play. Could anyone shed light on these numbers. Or the accuracy.

Any insight or explanation on why these numbers would be off/accuracy would be great!

Thanks!!
-D

Sayf Sharif Sayf Sharif says:

Dave,
It would be hard to say based on that data, and without looking at the page. However let’s say that the 8 people watched the video all the way through. You’d be looking at 3 events each so 24 total events. It’s possible one person played and paused alot and it skewed all the totals. Very possible with 10 total visitors to the page.

Or maybe there is something going wrong with the tracking. That could be the case as well and you have multiple events firing for some reason.

If one of those users was you, and you were testing the tracking though, it might be you who was inflating the overall numbers.

Peter says:

Hi. I’d like to track plays on a YouTube playlist. Will your script work with the additional playlist value appended after the video id.

http://www.youtube.com/embed/6pW2bE61Z9U?list=UUHxcqJWQQcoVmVh5eHVqVxg

There were additional attributes (like fullscreen, frameborder, etc) but I’ve removed them to the basic needed to play the video playlist.

Thanks,
Pete

Sayf Sharif Sayf Sharif says:

Peter,

It should match that and work out of the box.

Sayf Sharif Sayf Sharif says:

The core part to have is this:

//www.youtube.com/embed/6pw2bE61Z9U

whether it starts with nothing, http https, and whatever comes after it.

Leonardo says:

Hi Sayf, Is it possible to use this way with Google Tag manager? Ia am testing pasting the code int HTML Custom (GTM).

Dave says:

Hello,
It seems on Saturday the events went down to under 15 a day. Where before we were over 500 events a day. Did youtube change their embed? Is other peoples analytics counting the video views correctly since saturday?

Thanks.
-Dave

Jesse says:

Hi Sayf,
I trying to include this script in my wordpress site but it’s making the website crash.

I took the 2 script lines and put them at the bottom of the header.php file.
the last lines of the header.php looked like this:

?>

Jesse says:

Oh, I see I can’t post html here so I’ll just post a link to a text file of my header.php.
http://www.basketballworkouttips.com/wp-content/uploads/header.txt

Mike says:

Sayf — I’ve been using this for a few months now, so thank you for that. However, it suddenly stopped working recently. Was there a change at Google? It doesn’t seem to be working on this page right now either…

Sayf Sharif Sayf Sharif says:

Jesse, move the scripts. They won’t work when they are in the php portion of the code. copy them away from there and further up near the middle of the coe put them between an earlier close php and the end of the head tag.

?>
<—-put them here
</head>

Sayf Sharif Sayf Sharif says:

MIke, try the newest code we link above. Google changed how they embed the vidoes a little bit and the newer code may solve your problem.

Alvin says:

I used your code and it works fine on localhost but not when the code is live. Do you have any idea how I can make it work live?

Sayf Sharif Sayf Sharif says:

Alvin I can’t be sure what you are doing wrong. Could be that you are pointing to a local file in the script, but that is breaking when you make the code live.

Sayf Sharif Sayf Sharif says:

Leonardo, yes you can use this with GTM.

Nate says:

Hi Sayf-

I have a dynamic page that loads videos into the page without reloading the entire page via ajax.

I try to re-initalize the trackYouTube() function via JS when a new video is loaded, but I continually get ‘function not defined’ errors.

I have tried loading the script in different places (header, body, footer) to no avail. Do you have any insight on how to re-initialize the script when a new video is embedded/loaded?

Thank you kindly.

http://www.craftbeer.com/category/videos

Jesse says:

Hi Sayf, I moved the lines upwards like you said but the code is not working, I don’t see any events on analytics. Am I missing something?
http://www.basketballworkouttips.com/

Sayf Sharif Sayf Sharif says:

Jesse you have some extra double quotes showing up in your code so it’s broken both for jquery and the video script. Have only one set of plain double quotes where you have two.

Sayf Sharif Sayf Sharif says:

Nate,

Your best bet would be to write up some code that had a function called when the new video was dynamically written and then have that video added to the already existing array.

Or if you are only doing the dynamic load once, you could change our code to fire as a function when you call it rather than on document.ready which would be before the videos are loaded.

Graeme Mac says:

Thanks for the code. Do you know if anyone has added to the tracker to get view range into Google Analytics?

Ian L says:

Would be great if someone could expand on tracking YouTube videos using GTM! I’ve tried but failed to implement the above solution (it’s throwing errors)..

Sayf Sharif Sayf Sharif says:

We’ve actually implemented it through GTM as have others, and it should work right out of the box just include the code and go.

Make sure you have the right copy of the code (universal or classic) and then host that file on your own server, and include the script include code in a GTM tag.

If you are getting errors you are probably either not hosting the right code locally, or there is an error in how you are including the script via javascript, or your youtube embeds are being done through a different method where the iframes don’t exist when the tracking code runs through jquery.

Ian L says:

Thanks Sayf, I’ll give it another crack. 2 quick questions:
1) Is there a way to get this functioning without jquery if a site doesn’t have it? For instance, if all videos are in a particular subfolder? Or is the jquery doing more than just finding the iframes?
2) What specifically needs to be done in GTM to tie it all together?

Thanks again!

Sayf Sharif Sayf Sharif says:

Ian,

This code specifically requires jquery. The basic idea was that you could add this to a site, and if a content editor embeded a youtube video it would automatically be tracked just by embedding it in a blog post. If you KNOW all your videos you can use the youtube tracking api I link to above and just hardcode them all without using any jquery.

All that needs to be done in GTM is to add the script to a tag, and include it on any page you want your embeds to be tracked. that assumes you are already including jquery somewhere, otherwise you’d need to include that in a GTM tag as well.

Christian says:

Hi Sayf,

first of all thanks for your effort. I tried keeping track of the 3 embeded Youtube Videos on this site:
http://www.amd-promotion.de/bf4/

For some unknown reason it doesn’t work. Iam about to give up because i dont see it. Maybe you can help?

thanks in advance!

Sayf Sharif Sayf Sharif says:

Christian, it looks to me like it’s working. What browser are you using?

Christian says:

Hi Sayf,

iam using Chrome.
All of a sudden without any changes it works now… WOOHOO ^^
Thanks alot for sharing that script!

Artur Barseghyan says:

Hey Sayf. I’m testing your code with official google analytics tool and I don’t seem to get any tracking when clicking youtube videos. All scripts are placed in the head. Also, on this page it doesn’t work either, since at the moment your CDN is down. Did you try it very recently?

Artur Barseghyan says:

More about this. Likely, in my case, the YouTube video is covered by some transparent element which prevents the tracking. I didn’t find the solution yet. At the moment when I remove all the CSS declarations, the script starts working. I did try to set z-index of the iframe to 999999 and the wmode to opaque or transparent. None of those tricks worked. Do you have any tips? Thanks!

Christian says:

I end up with a couple errors in chrome that prevent the API events from firing. Any suggestions

Uncaught SecurityError: Blocked a frame with origin “http://www.youtube.com” from accessing a frame with origin “http://www.domain.com”. Protocols, domains, and ports must match.

Uncaught TypeError: Cannot read property ‘classList’ of null

Hao says:

Hi Sayf,
Thank you for your script! I put your script in head of our velocity template file, then play the youtube video in this url http://www.stripes.com/forging-honor-how-the-military-medals-are-made-1.225411, then I check in Google analytics. I could not see the event in Top Events, could you tell me on this?

Sayf Sharif Sayf Sharif says:

hey guys and gals,

Something appears to have changed again and it’s not working as intended. I am looking into it.

ksampath says:

Hi Sayf,
I added this to the blog using Google Tag Manager. The script is firing as it should but when i see the network section in google developer’s tools it shows a 404 error to fetch the JS. What is happening is, say for eg it is hosted in example.com/js/lunametrics-youtube-v5.js(i tested the url and it is returning a 200.). But when the browser loads it adds the page of that url and then this script url. Say i was browsing on example.com/foo.html then it the browser tries to find the script on example.com/foo.html/example.com/js/lunametrics-youtube-v5.js(obviously there is no suck url like this and hence it returns a 404). Any idea why?

jake says:

Hi Sayf,

Thanks for the code. I put it in my wordpress site’s header.php file a few days ago, but I’m not seeing new events appear in my Google Analytics dashboard. Was I supposed to do anything in GA, or did I miss a step?

Thanks again

Sayf Sharif Sayf Sharif says:

Hey, as I mentioned there is something currently going on and the code might not be working. I am investigating. If anyone else finds a solution please post it here.

Mick says:

Thanks for your script. Each time i try to track something with an event, I have the following warning : ReferenceError: ga is not defined
Thanks for help

Sayf Sharif Sayf Sharif says:

Mick you might be using the wrong tracker? if you are using asynchronous tracking you need the async include, not the universal one.

Steve Austin says:

Hi Sayf,

I have 2 issues with this.

1) Up until today, it works (albeit inconsistently) across different browsers.
To summarise: Google Chrome – works fine for direct youtube in-page embeds and when embedded into rotating panels, Firefox – works fine direct page embed but not always when embedded within rotating panels, IE – intermittent and unpredictable

2) The second issue brought to attention only today is that the tracking was reported to not be working at all now. After debugging in Firefox I noticed that the onYouTubeIframeAPIReady() method is very rarely being invoked, therefore the tracking results are also rarely triggered.

Any suggestions would be greatly appreciated.

The code I am using is below (apologies for the commented out lines of code):

/*
YouTube Analytics
Code adapted from:
http://www.lunametrics.com/blog/2012/10/22/automatically-track-youtube-videos-events-google-analytics/
http://lunametrics.wpengine.netdna-cdn.com/js/lunametrics-youtube.js
Code adapted by Alex Mueller for ISITE Design http://isitedesign.com
*/

// enable cross-domain scripting in IE -1) {
// …remove the extra characters
videoID = videoID.substr(0, videoID.indexOf(‘?’));
}

// put an object in our array with the videoID…
videoArray[i] = {};
videoArray[i].id = videoID;

// …and the video title (pulled from the YouTube data API)
$.ajax({
dataType: ‘JSON’,
type: ‘GET’,
url: url
})
.done(function(data) {
videoArray[i].title = data.entry.title.$t;
});

// put the videoID on the iframe as its id
$iframe.attr(‘id’, videoID);
}
});
}

$(function() {
// initiate tracking on document ready
trackYouTube();

//may not be needed – check everything reloaded when MSP including video is clicked
// $(‘a[href^=”http://#$$”]’).click(function () {
// var listItem = $(this).parent();
// //alert(listItem[0].nodeName.toLowerCase());
// var idx = $(‘ul.tabs li’).index(listItem);
// var slctdTab = $(‘.tabbed-box .tabbed-content:eq(‘+idx+’)’);
// if ((slctdTab).find(‘iframe[src*=”youtube”]’).length) {
// alert(‘youtube vid found’);
// trackYouTube();
// }
// });

});
})(jQuery);

// when the YouTube iframe API has loaded
function onYouTubeIframeAPIReady() {
// insert YouTube Player objects into our playerArray
for (var i = 0; i < videoArray.length; i++) {
playerArray[i] = new YT.Player(videoArray[i].id, {
events: {
//'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
}

// when the player changes states
function onPlayerStateChange(event) {
var videoIndex = event.target.id – 1;

// if the video begins playing, send the event
if (event.data == YT.PlayerState.PLAYING) {
_gaq.push(['_trackEvent', 'Videos', 'Play', videoArray[videoIndex].title]);
videoPlaybackInProgress = true;
stopRotation();
//alert(videoArray[videoIndex].title);
}
// if the video paused, send the event
if (event.data == YT.PlayerState.PAUSED) {
//_gaq.push(['_trackEvent', 'Videos', 'Paused', videoArray[videoIndex].title]);
startRotation();
//alert('video paused');
}
// if the video ends, send the event
if (event.data == YT.PlayerState.ENDED) {
_gaq.push(['_trackEvent', 'Videos', 'Ended', videoArray[videoIndex].title]);
startRotation();
}
}

function onPlayerReady(event) {
event.target.playVideo();
}

Sayf Sharif Sayf Sharif says:

Steve,

Something changed in the process, and now the script isn’t working, or isn’t working consistently. We are investigating a fix, but as of right now this code is definitely not working. If you figure something out please come back and post it here and we can incorporate it!