2

I built a player on top of videoJS and I'm having trouble accessing public functions inside videoJS .ready(). The thing is that my code appears to be working everywhere except IE (works in chrome, safari, ff, etc.):

var myPlayer = _V_('myvideojsId');
myPlayer.ready(function() {
    var player = this;
    player.myPublicFunction = function() {
        alert("hey!");
    }
});

myPlayer.myPublicFunction();

In IE I get

Object does not support this property or method

on the myPlayer.myPublicFunction() line. Are the other browsers letting me get away with bad code or is this IE's fault?

Any help would be great, thank you!

Chris

xlecoustillier
  • 16,183
  • 14
  • 60
  • 85
Chris
  • 23
  • 4

2 Answers2

2

Referencing their documentation, it shows exactly what Jonathan has said: https://github.com/zencoder/video-js/blob/master/docs/api.md#wait-until-the-player-is-ready

He's right about IE by the way. As much as we all love to hate it, it has found real issues for me many times.

Just for quicker reference, here's an alternative to your method to getting this done:

_V_("example_video_1").ready(function(){

  var myPlayer = this;

  // EXAMPLE: Start playing the video.
  myPlayer.play();

});
Douglas
  • 69
  • 1
  • 4
1

This is likely a problem with timing:

myPlayer.ready(function() {});
myPlayer.myPublicFunction();

Your first line here hands off a function to myPlayer to call whenever the player is ready. This doesn't happen immediately in most cases, so there is most likely a delay. This means your public function isn't added to the myPlayer object immediately, but rather this task will be accomplished whenever the video player is ready.

All of this means that when JavaScript moves on to the second line, the appropriate response from a browser is that the method doesn't exist - because it doesn't. It won't exist until the video player is ready, which isn't until later.

You could use more of a feature-detection approach, and only call the method if it exists:

if (myPlayer.myPublicFunction) {
    myPlayer.myPublicFunction();
}

You could also just add the method before-hand:

myPlayer.myPublicFunction = function () { alert("Foo"); };
myPlayer.ready(callback);
myPlayer.myPublicFunction(); // 'Foo'

In the end, I've found that Internet Explorer is not as forgiving (which is good) as some other browsers. If it's acting up today, it's likely because there's a problem in the code.

Sampson
  • 265,109
  • 74
  • 539
  • 565
  • Thanks Jonathan... that works for just callback, but inside the ready & my public function I was overriding some set/defined vars that were defined after the .ready() fires... let me write up a better example... – Chris Dec 05 '12 at 16:28
  • for example: player = this; seekPosition = 0; and then I'd set or override the seekPosition value via something like myPlayer.setSeekPosition(10); and my public function inside ready() would be player.setSeekPosition = function(val) { seekPosition = val; }); ... so what you're saying makes sense as far as firing before ready() but I then I can't wrap my head around externally setting internal properties/vars defined inside ready() – Chris Dec 05 '12 at 16:35
  • @Chris Why are you adding that function within the ready callback? Why not add it beforehand like I did above? – Sampson Dec 05 '12 at 16:37
  • Because based on the videoJS documentation properties, functions, etc. appear to be defined inside .ready(), https://github.com/zencoder/video-js/blob/master/docs/api.md ... because before you can tack on functionality the videoJS player has to be ready. – Chris Dec 05 '12 at 16:42
  • @Chris That's not the case; in JavaScript you can add methods onto objects at any time. The documentation you provided only shows them calling certain methods from within the `ready` method. This makes sense, since the object might not be ready at an earlier time. You can *add* methods whenever you like though. – Sampson Dec 05 '12 at 16:44
  • I think I need to rework my logic. I dumped everything in to the ready() and everything was working except IE. I setup a custom init-like function that's called back after ready() fires, and I can access some defined public vars but still can't access functions defined inside ready(). At any rate I'm making progress... I could have left this crap alone had it not been for IE :(. Thanks Jonathan. – Chris Dec 05 '12 at 17:25
  • @Chris IE isn't the problem here. This is *JavaScript* that you're having problems with. JavaScript is asynchronous in many cases. The method you're adding to `this` doesn't exist when you're calling it. If any browser doesn't report this as an error, it means the browser was too slow to call the method (allowing time for it to be setup beforehand via the .ready method), or that the browser isn't informing you that the method doesn't exist. Either way, IE is not at fault. – Sampson Dec 05 '12 at 17:28
  • Right, I know it's me, not IE. IE is making me work harder that's all :) – Chris Dec 05 '12 at 17:35
  • @Chris IE is making you play by the rules ;) It's tough, but it's for our own good. – Sampson Dec 05 '12 at 17:37
  • I'm still looking at my problem. I narrowed my code to not working in IE8 (and probably IE7). IE9 and IE10 actually run and report the public functions inside my ready() with out error. Oh well... I'll keep hacking away. – Chris Dec 05 '12 at 21:31
  • @Chris The problem is that `.ready` isn't called immediately in all cases (it could be in some, but that's not necessarily normative). I cannot see any reason why you're adding the function to the object so late; you can add it sooner. The documentation doesn't suggest you need to wait for the ready method in order to add more functions. – Sampson Dec 05 '12 at 21:33
  • Because (a) my knowledge of OOP is limited, and (b) because the videoJS object, once it's ready() I set up a bunch of private vars, private functions, and event listeners. Then I set up some public functions that manipulate private vars. I then create another object (myVideo) and point it at the videoJS object. I then want to have different DOM elements (click event f(), etc) manipulate the private vars available to videoJS through the public functions I made available when videoJS became ready(). Anyway that's the idea so I can package the sections of code individually. – Chris Dec 05 '12 at 22:10
  • It's essentially what my example is showing, just with out the checks and balances that I need in place. I'm struggling with writing it the "correct" way. – Chris Dec 05 '12 at 22:13
  • If you paste my example, it works. It may be incorrect, but it works as intended just about everywhere. I'd like to get that working the right way. If an example of that exists, that would be great :) – Chris Dec 05 '12 at 22:23
  • @Chris You can create the method to override the seek position at any time. You don't have to do that from within the ready callback. If you show me exactly the code you're trying to run, I can modify it for you such that it shouldn't have any issues. – Sampson Dec 06 '12 at 15:22
  • It's about 500 lines. My example basically sums it up. In ready() I have video vars and functions. One of which is, for example player.setStartPosition = function(val) { startAtPos = val; } startAtPos is a private var defined in .ready(), and setStartPosition is the public function I'm trying to access outside of .ready() to set/override the private var. There is a great function here http://stackoverflow.com/questions/152483/is-there-a-way-to-print-all-methods-of-an-object-in-javascript that confirms my methods are defined correctly. It only spits out the public methods I defined. – Chris Dec 06 '12 at 16:03
  • I had a myPlayer.myInit = function() {} defined outside of ready(), and then in ready() I called it via player.myInit();. That works, and I can access public vars defined inside ready() like player.pulicVar = "something" and I can read it out inside the myInit function. That's great, but then I still can't call a public method defined inside ready() from within the myInit function. – Chris Dec 06 '12 at 16:08
  • By the way, this entire thing is wrapped in document.ready(). Perhaps thats why all of the other browsers are not having an issue with the code. – Chris Dec 06 '12 at 16:18
  • @Chris Is this page online someplace? – Sampson Dec 06 '12 at 16:22
  • Not yet, on my local server. Also, It tried your feature detect method, and it solves IE8 from throwing an error, but that's all. I even tried some setTimeout hackjobs to exec access to the would-be functions to just test and see if it makes a difference in IE8 reading code beforehand and that does not solve. Jonathan, again if you take the code in my first example... really the only thing it's missing from the logic is that the entire thing is wrapped in $(document).ready(my example code). – Chris Dec 06 '12 at 16:34
  • @Chris What is the value of `this` in your first example? I've setup my own demo, not having access to yours. It communicates my concern: http://jsfiddle.net/j2zhP/ – Sampson Dec 06 '12 at 16:39
  • this is = to _V_('myvideojsId'); You need to load videoJS if you want to set it to the videoJS object.... let me look at your fiddle... I'm basically extending the functionality of videoJS by adding more methods. – Chris Dec 06 '12 at 16:43
  • @Chris Within your ready callback `this` will only be the video player if the callback was called with `.call`, having the local `this` passed in as the first argument. If you don't mind, from within your callback, write `console.dir(this)` and confirm that it is the player, and not something like `Window`. – Sampson Dec 06 '12 at 16:56
  • Yeah I think I understand what you're doing with the delays. The problem is I don't you can prototype out the videoJS object ready f() as you had in the example. Throws type and undefined errors. I think videoJS throws a monkeywrench in to this where by normally what you have works. – Chris Dec 06 '12 at 16:57
  • ok so in my callback function: myPlayer.myInit = function() { console.dir(this); } defined outside of the ready() (but called after ready() fires), I get a class stack: Class el:
    flashSrc: function (type,src) { iPhoneSrc: function (type,src) { id: "vslice" isReady: true manualProgress: false manualTimeUpdates: false myPublicFunction: function () { options: Object playSection: function (start,end,loop) { playerInit: function () { progressInterval: 36 publicVar: 10 readyQueue: Array[0] setHiddenOptions: function (arr) { setMobilePlayButton: function (o) { etc. matches the videoJS object
    – Chris Dec 06 '12 at 17:05
  • 'vslice' in that class stack is the real ID name... in my example i used "myvideojsId" so... again because I simplified it, sorry. Here is the .log version: `Class {tag: – Chris Dec 06 '12 at 17:15
  • weird. I see my public functions in the class stack, but I cant access them. console.log(this.myPublicFunction) results in undefined, yet I can access other vars and functions. I'm wondering if I'm just not defining them correctly from the get go. Progress :) – Chris Dec 06 '12 at 17:32
  • Jonathan I got it working with you jsfilddle example! I honestly tried the setTimeout before and I could not get it working. I tried it again but now *within the callback init function in place with a 1 second delay and it works! It accesses the public function within ready() A-OK. Man thank you! You dealt all this time with my stupidity :) – Chris Dec 06 '12 at 19:40
  • @Chris I'm pleased to hear that I was able to help; when you have a bit more reputation you'll be able to get into the chat rooms here, which makes solving problems like this much easier. Hope to run into you from time to time again in the future! – Sampson Dec 06 '12 at 19:46