Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Harlowe - Play a sound on a cycling link

This is more of a request for advice on best practice. I have achieved what I wanted to achieve, but in a shambolic way that I expect may cause problems later down the line.

I want a cycling link that plays a 'clicking' sound every time you click on it, simulating a combination lock.

I have achieved this by combining Furkle's cycling link Macro (http://furkleindustries.com/fictions/twine/twine2_resources/twine2_macros/) with this guide as to getting a sound to play onclick (http://stackoverflow.com/questions/18826147/javascript-audio-play-on-click)

*However*, to get this to work, I have ended up with the cyclinglink JS in the Story Javascript window, and an additional separate piece of script in the particular passage I am working in. For reasons I don't understand, the audio play script does not work when I add it into the Story Javascript window.

So I have this at the top of my passage:
<script>
  function play(){
       var audio = document.getElementById("audio");
       audio.play();
                 }
</script>
<audio id="audio" src="sound/click.wav"></audio>

And then each of the tumblers in my lock look like this:
(set: $lock01 to "1")<tw-link class='cyclingLink' data-cycling-texts='["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]' onclick='play();clickCyclingLink(this, "$lock01");'>$lock01</tw-link>

As I understand it, this is not good JS. I should be using onclick to call a single function that then contains multiple subfunctions within it. However, when I try to move the play function JS into Furkle's JS, it doesn't work. Neither does it work when I try to create a combined function at the top of the passage like this:
<script>
  function play(){
       var audio = document.getElementById("audio");
       audio.play();
       clickCyclingLink(this, "$lock01");
                 }
</script>

What's the cleanest way out of this muddle?

Comments

  • edited May 2016
    note: I am not going to change your function's content, even though it does not have any error checking.

    Your issue is one of Scope.

    Basically the things you define (like your function) in the Story Javascript area are in a different Local scope than that of your Passage's content, so the passage code can't access the function.

    There are a number of ways to raise the scope of the function to Global, the most common is to add the definition of your function to an object that is already Global like window (and this is what Furkle's code is doing)
    /* Add your play function to the window object if it does not already have one. */
    if (! window.play) {
    	window.play = function () {
    		var audio = document.getElementById("audio");
    		audio.play();
    	};
    }
    
    ... you can now assess your play function globally.

    WARNING: Generally it is a bad idea to add new properties directly to the window object because they may clash with existing/future web-browser properties. The MDN JavaScript Object Management page explains different ways to manage your own objects, one of them being Namespaces.

    The following is a very basic implementation of a Namespace named My which contains your play function.
    if (! window.My) {
    	window.My = {
    		play: function () {
    			var audio = document.getElementById("audio");
    			audio.play();
    		}
    	}
    }
    
    ... you would access this version of your play function via My.play() instead.
  • edited July 2016
    Oh man. It seems we have been trying to get the exact same effect but I somehow missed this post.

    I posted last week but no one replied.

    My scripting skills are almost nil, but I achieved the effect by basically having the page refresh after each click and then having the sound play upon each load of the page. It's probably a poor hack in scripting terms but surprisingly effective and I'm chuffed with the result.

Sign In or Register to comment.