Search code examples
javascriptyoutubegetter-setter

Overwriting a property on a HTMLVideoElement to a getter/setter with custom logic


What I want to accomplish:

I'm trying to write an extension for chrome that adds some extra functionality to Youtube. This particular problem has to do with knowing when the playback rate has been changed by the user (which can be done by clicking the settings icon, then speed).

What I've tried to do:

I'm trying to do this by replacing the "playbackRate" property on the HTMLVideoElement with a getter/setter object so that I can detect when this happens.

The Problem:

When the user changes it, all my logic runs, however the video will no longer actually change speed.

The Code:

    let videoObj = document.getElementsByTagName('video')[0];
    let storePlaybackRate = videoObj.playbackRate;
    Object.defineProperty(videoObj,'playbackRate',{
        get(){return storePlaybackRate;},
        set(val){
            storePlaybackRate = val;
            console.log("Updated Value: ",val);

            //Custom Logic that does work.

            return val;
        }
    });

To test this just paste the code into the console on any youtube video, then change playback speed. The console.log happens, but the video no longer changes speed.

Things I already checked

1.That this property is not already a getter/setter object that I'm overwriting. This is checked via:

Object.getOwnPropertyDescriptor(videoObj, 'playbackRate');

2.I thought maybe it was because I wasn't returning the new value in the setter, but that wasn't it.

Final Summary

I understand there are other ways of doing what I'm trying to do, and so if this is impossible by this method, that's fine. But I just really wanted to understand why this doesn't work. Or if I made an error.

Thanks.


Solution

  • You are overwriting an inherited getter/setter.

    An HTMLVideoElement does not have its own getter/setter for playbackRate, but inherits one from HTMLMediaElement.prototype. Thats why Object.getOwnPropertyDescriptor(video,'playbackRate') returns undefined

    To overwrite it, you have to store the original descriptor, and call them within your own descriptor.

    originalDescriptor = Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'playbackRate');
    
    Object.defineProperty(HTMLVideoElement.prototype,'playbackRate',{
        set:function(value){
            originalDescriptor.set.call(this,value);
            console.log('value changed',value);
        },
        get:originalDescriptor.get
    });
    

    I've used the prototype for the video in this example, it is possible to adjust the descriptor for a specific element by replacing HTMLVideoElement.prototype with the video element's reference.