0

I'm defining a class in Javascript meant to serve as an audioplayer compatible with iOS. I'm just getting started with the basics, and running into an issue when trying to access a class method.

After creating an instance of the class (var audio = new WebAudio('soundfile.mp3', document.querySelector(#sound_div)), and attempting to access the method audio.playSound(), I'm getting:

ReferenceError: Can't find variable: elem on line 29

class WebAudio {

    constructor(soundFile, elem) {
        this.soundFile = soundFile;
        this.elem = elem;
    }

    context() {
        var AudioContext = window.AudioContext || window.webkitAudioContext;
        var context = new AudioContext();
        return context;
    }

    webAudioTouchUnlock(context) {
       return new Promise(function (resolve, reject) {
       //if AudioContext is suspended, and window has been interacted with
           if (context.state === 'suspended' && 'ontouchstart' in window) {
           console.log(context.state);
           var unlock = function() {
               //resume AudioContext (allow playing sound), remove event listeners
               context.resume().then(function() {
                   console.log("context resumed");
                   this.elem.removeEventListener('touchstart', unlock);
                   this.elem.removeEventListener('touchend', unlock);
                   resolve(true);
               }, function (reason) {
                   reject(reason);
               });
           };
           this.elem.addEventListener('touchstart', unlock, false); //error
           this.elem.addEventListener('touchend', unlock, false);
           } else {
               console.log('context not suspended? Context is ' + context.state);
               resolve(false);
           }
       });
    }

    playSound() {
        this.webAudioTouchUnlock(this.context()).then(function (unlocked) {
            if(unlocked) {
                console.log('playing audio file');
                var audio = new Audio('sound/' + soundFile);
                if (!audio.playing) {
                    audio.play();
                } else {
                    audio.pause();
                }
            }
        }, function(reason) {
            console.error(reason);
        });
        document.body.addEventListener('load', playSound(soundFile));
    }
}
2
  • What's some example input? What arguments would you pass to new WebAudio? Commented May 26, 2019 at 22:39
  • @JackBashford thanks for the reply, usage is in the question. "var audio = new WebAudio('soundfile.mp3', document.querySelector(#sound_div)" Commented May 26, 2019 at 22:42

1 Answer 1

2

You lose the binding to this when you pass the function to an event listener:

var unlock = function() {
               //resume AudioContext (allow playing sound), remove event listeners
               context.resume().then(function() {
                   console.log("context resumed");
                   // this won't point to the instance when called by listener
                   this.elem.removeEventListener('touchstart', unlock);
                   this.elem.removeEventListener('touchend', unlock);
                   resolve(true);
               }, function (reason) {
                   reject(reason);
               });
           };
           this.elem.addEventListener('touchstart', unlock, false); //error

Arrow functions or manually calling bind(this) can fix it. The arrow function will bind this in the function lexically, which means this will be the this value from where it was defined rather than how it is called:

var unlock = () => {
               //resume AudioContext (allow playing sound), remove event listeners
               context.resume().then(() => {
                   console.log("context resumed");
                   this.elem.removeEventListener('touchstart', unlock);
                   this.elem.removeEventListener('touchend', unlock);
                   resolve(true);
               }, function (reason) {
                   reject(reason);
               });
           };
           this.elem.addEventListener('touchstart', unlock, false); //error 
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks, now I'm getting TypeError: undefined is not an object (evaluating 'this.elem') — webAudio.js:29 - audio.elem holds the correct reference though
@froggomad sorry, there's a lot of nested functions here. You probably need to defined you Promise callback with an arrow function too: return new Promise((resolve, reject) => {...
Thanks, you lead me on the right path... couldn't have done it without you. Needed this.webAudioTouchUnlock(this.context()).then( (unlocked) => in playSound() and a separate function to add the listener
Cool, glad you figured it out @froggomad.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.