0

I have a grid of buttons in my Javascript Windows app using WinJS.

    <div class="padtable">
        <button class="pad" id="pad11">Empty</button>
        <button class="pad" id="pad12">Empty</button>
        <button class="pad" id="pad13">Empty</button>
        <button class="pad" id="pad14">Empty</button><br />
        <button class="pad" id="pad21">Empty</button>
        <button class="pad" id="pad22">Empty</button>
        <button class="pad" id="pad23">Empty</button>
        <button class="pad" id="pad24">Empty</button><br />
        <button class="pad" id="pad31">Empty</button>
        <button class="pad" id="pad32">Empty</button>
        <button class="pad" id="pad33">Empty</button>
        <button class="pad" id="pad34">Empty</button><br />
        <button class="pad" id="pad41">Empty</button>
        <button class="pad" id="pad42">Empty</button>
        <button class="pad" id="pad43">Empty</button>
        <button class="pad" id="pad44">Empty</button><br />
    </div>

Then I try to cycle through all the buttons, and if it is associated with a file, I try to get the file as an audio tag, and append it to it. Except that getting the file takes too long, so instead of appending the audio tag to each individual button, it appends all audio tags to the last button. How can I work around this? Here is the code that does the appending:

var pads = document.querySelectorAll(".pad");
            // Cycle through all pads, attaching audio tags where mappings exist
            for (var padCount = 0; padCount < pads.length - 1; padCount++) {
                // Find out if the mapping exists
                var fileMappingExists = false;
                if (fileMappings[pads[padCount].id] != null) {
                    fileMappingExists = true;
                }

                // If the file mapping exists, get the file from the FAL, and create audio tags tied to their respective buttons
                if (fileMappingExists) {
                    Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList.getFileAsync(fileMappings[pads[padCount].id].audioFile).done(function (audioFile) {
                        if (audioFile) {
                            var audioId = "audioPlayer_" + pads[padCount].id;
                            var audioPlayer = document.createElement('audio');
                            audioPlayer.setAttribute("id", audioId);
                            audioPlayer.setAttribute("controls", "true");
                            audioPlayer.setAttribute("style", "display:none;");
                            audioPlayer.setAttribute("msAudioCategory", "SoundEffects");
                            audioPlayer.src = URL.createObjectURL(audioFile, { oneTimeOnly: true });
                            document.getElementById(pads[padCount].id).appendChild(audioPlayer);
                            audioPlayer.load();

                            // Assign button click to song play
                            // TODO | BUG: Click events and audio tags are only assigned to last pad because of getFileAsync
                            var pad = document.getElementById(pads[padCount].id);
                            pad.addEventListener("click", function () {
                                // Set audio to start if it's already playing
                                if (audioPlayer.currentTime != 0)
                                    audioPlayer.currentTime = 0;

                                // Play the file
                                audioPlayer.play();
                            }, false);

                            WinJS.log && WinJS.log("Audio file was added successfully for" + pads[padCount].id, "sample", "status");
                        }
                        else {
                            document.getElementById("output").innerText += "\nAudio file not found for " + pads[padCount];
                        }
                    });


         }
}
Freakishly
  • 1,533
  • 5
  • 32
  • 61
  • I don't think being "slow" has anything to do with this. In all of this code, where is the actual problem taking place? Where are you appending the element and how do you identify where to append it? – David Jun 03 '14 at 17:38
  • The variable `padCount` will be equal to `pads.length - 1` each time your `.done` function runs. – James Jun 03 '14 at 17:41

1 Answers1

1

To understand what is going on here read this

Here's one way of approaching a fix:

// If the file mapping exists, get the file from the FAL, and create audio tags tied to their respective buttons
if (fileMappingExists) {
    Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList.getFileAsync(fileMappings[pads[padCount].id].audioFile).done(
        (function (pc) {
            return function (audioFile) {
                if (audioFile) {
                    var audioId = "audioPlayer_" + pads[pc].id;
                    var audioPlayer = document.createElement('audio');
                    audioPlayer.setAttribute("id", audioId);
                    audioPlayer.setAttribute("controls", "true");
                    audioPlayer.setAttribute("style", "display:none;");
                    audioPlayer.setAttribute("msAudioCategory", "SoundEffects");
                    audioPlayer.src = URL.createObjectURL(audioFile, { oneTimeOnly: true });
                    document.getElementById(pads[pc].id).appendChild(audioPlayer);
                    audioPlayer.load();

                    // Assign button click to song play
                    // TODO | BUG: Click events and audio tags are only assigned to last pad because of getFileAsync
                    // FIXED AS WELL
                    var pad = document.getElementById(pads[pc].id);
                    pad.addEventListener("click", (function (ap) {
                        return function () {
                            // Set audio to start if it's already playing
                            if (ap.currentTime != 0)
                                ap.currentTime = 0;

                            // Play the file
                            ap.play();
                        };
                    })(audioPlayer), false);

                    WinJS.log && WinJS.log("Audio file was added successfully for" + pads[pc].id, "sample", "status");
                }
                else {
                    document.getElementById("output").innerText += "\nAudio file not found for " + pads[pc];
                }
            };
        })(padCount);
    );
}
Community
  • 1
  • 1
James
  • 20,957
  • 5
  • 26
  • 41
  • Thank you, that worked perfectly. And I had no idea about closures, so thank you for the link. – Freakishly Jun 03 '14 at 19:25
  • One more comment here (since I need to remove the click event listener as well). I moved the function to a separate place in the code, and call it by pad.addEventListener("click", playAudioFileOnClick(audioPlayer), false); playAudioFileOnClick has the same definition as above. However, when I try pads[padCount].removeEventListener("click", playAudioFileOnClick, false); it does not work, i.e. it still plays the audio file. I have even used var audioElement = document.getElementById(audioId); pads[padCount].removeChild(audioElement); to remove the audio tag but it still plays – Freakishly Jun 03 '14 at 20:05
  • You would need to set a variable for the callback function, eg `var listener = playAudioFileOnClick(audioPlayer);`, then `pad.addEventListener("click", listener, false);` and similarly `pad.removeEventListener("click", listener, false);` – James Jun 03 '14 at 20:29
  • what about the parameter, i.e. playAudioFileOnClick(audioPlayer). Will pad.addEventListener("click", listener(audioPlayer), false) work? – Freakishly Jun 03 '14 at 20:33
  • The parameter worked, but pad.removeEventListener("click", listener, false) didn't. Maybe this warrants a separate question. Thanks for the help, James. – Freakishly Jun 03 '14 at 20:35
  • I would check to make sure that listener is still in scope where you are removing it. And you're welcome. – James Jun 03 '14 at 20:37
  • listener is definitely in scope. For now I'm setting the audio src to "". This is only a temporary solution. – Freakishly Jun 03 '14 at 23:39