简体   繁体   中英

How to change value of global var when it is being used as a parameter in a function? (Javascript)

I want to be able to change the value of a global variable when it is being used by a function as a parameter.

My javascript:

function playAudio(audioFile, canPlay) {

    if (canPlay < 2 && audioFile.paused) {
        
        canPlay = canPlay + 1;
        audioFile.play();

    } else {

        if (canPlay >= 2) {
            alert("This audio has already been played twice.");
        } else {
            alert("Please wait for the audio to finish playing.");
        };

    };

};

const   btnPitch01        = document.getElementById("btnPitch01");
const   audioFilePitch01  = new Audio("../aud/Pitch01.wav");
var     canPlayPitch01    = 0;

btnPitch01.addEventListener("click", function() {

    playAudio(audioFilePitch01, canPlayPitch01);

});

My HTML:

<body>

    <button id="btnPitch01">Play Pitch01</button>
    <button id="btnPitch02">Play Pitch02</button>

    <script src="js/js-master.js"></script>

</body>

My scenario:

I'm building a Musical Aptitude Test for personal use that won't be hosted online. There are going to be hundreds of buttons each corresponding to their own audio files. Each audio file may only be played twice and no more than that. Buttons may not be pressed while their corresponding audio files are already playing.

All of that was working completely fine, until I optimised the function to use parameters. I know this would be good to avoid copy-pasting the same function hundreds of times, but it has broken the solution I used to prevent the audio from being played more than once. The "canPlayPitch01" variable, when it is being used as a parameter, no longer gets incremented, and therefore makes the [if (canPlay < 2)] useless.

How would I go about solving this? Even if it is bad coding practise, I would prefer to keep using the method I'm currently using, because I think it is a very logical one.

I'm a beginner and know very little, so please forgive any mistakes or poor coding practises. I welcome corrections and tips.

Thank you very much!

It's not possible, since variables are passed by value, not by reference. You should return the new value, and the caller should assign it to the variable.

function playAudio(audioFile, canPlay) {
  if (canPlay < 2 && audioFile.paused) {
    canPlay = canPlay + 1;
    audioFile.play();
  } else {
    if (canPlay >= 2) {
      alert("This audio has already been played twice.");
    } else {
      alert("Please wait for the audio to finish playing.");
    };
  };
  return canPlay;
};

const btnPitch01 = document.getElementById("btnPitch01");
const audioFilePitch01 = new Audio("../aud/Pitch01.wav");
var canPlayPitch01 = 0;

btnPitch01.addEventListener("click", function() {

  canPlayPitch01 = playAudio(audioFilePitch01, canPlayPitch01);

});

A little improvement of the data will fix the stated problem and probably have quite a few side benefits elsewhere in the code.

Your data looks like this:

const btnPitch01 = document.getElementById("btnPitch01");
const audioFilePitch01 = new Audio("../aud/Pitch01.wav");
var canPlayPitch01 = 0;

// and, judging by the naming used, there's probably more like this:
const btnPitch02 = document.getElementById("btnPitch02");
const audioFilePitch02 = new Audio("../aud/Pitch02.wav");
var canPlayPitch02 = 0;

// and so on

Now consider that global data looking like this:

const model = {
  btnPitch01: {
    canPlay: 0,
    el: document.getElementById("btnPitch01"),
    audioFile: new Audio("../aud/Pitch01.wav")
  },
  btnPitch02: { /* and so on */ }
}

Your event listener(s) can say:

btnPitch01.addEventListener("click", function(event) {
  // notice how (if this is all that's done here) we can shrink this even further later
  playAudio(event);
});

And your playAudio function can have a side-effect on the data:

function playAudio(event) {
    // here's how we get from the button to the model item
    const item = model[event.target.id];
    if (item.canPlay < 2 && item.audioFile.paused) {
        item.canPlay++;
        item.audioFile.play();
    } else {
        if (item.canPlay >= 2) {
            alert("This audio has already been played twice.");
        } else {
            alert("Please wait for the audio to finish playing.");
        };
    };
};

Side note: the model can probably be built in code...

// you can automate this even more using String padStart() on 1,2,3...
const baseIds = [ '01', '02', ... ];
const model = Object.fromEntries(
  baseIds.map(baseId => {
    const id = `btnPitch${baseId}`;
    const value = {
      canPlay: 0,
      el: document.getElementById(id),
      audioFile: new Audio(`../aud/Pitch${baseId}.wav`)
    }
    return [id, value];
  })
); 

// you can build the event listeners in a loop, too
// (or in the loop above)
Object.values(model).forEach(value => {
  value.el.addEventListener("click", playAudio)
})

below is an example of the function.

 btnPitch01.addEventListener("click", function() { if ( this.dataset.numberOfPlays >= this.dataset.allowedNumberOfPlays ) return; playAudio(audioFilePitch01, canPlayPitch01); this.dataset.numberOfPlays++; });

you would want to select all of your buttons and assign this to them after your html is loaded.

https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName

 const listOfButtons = document.getElementsByClassName('pitchButton'); listOfButtons.forEach( item => { item.addEventListener("click", () => { if ( this.dataset.numberOfPlays >= this.dataset.allowedNumberOfPlays ) return; playAudio("audioFilePitch" + this.id); this.dataset.numberOfPlays++; });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM