www.webdeveloper.com
Results 1 to 9 of 9

Thread: How to craft synchronous (asynchronous?) callback?

  1. #1
    Join Date
    Aug 2011
    Posts
    12

    How to craft synchronous (asynchronous?) callback?

    I need to create a callback for a line of code that performs asynchronous work so that another a line of code can be called after it is finished. I've found a number of webpages that attempt to show how this can be done with two functions, one calling the other in Russian-doll fashion, but I can't see how to do it with my code.

    Below is my code. It takes the URL of a sound file, redefines a previously defined embed to point at that sound file, and then plays it. The problem is that I lose focus on the documentElement that was selected before the playIt() function is called. So in the playIt() function I save the focused element in a variable and focus() it after the embed-switcheroo and autoplay is performed. This doesn't work, because the "e.parentNode.replaceChild(clone, e);" is performed asynchronously; when it finishes, it clears the focus in the document (internal id's have changed? Reason unknown.) So I need the focus() code to follow the replaceChild() code. I want to accomplish this by having the focus() code execute as a callback following the replaceChild() code. How would I break this into two procedures, one calling the other, reproducing a synchronous flow?


    Code:
    function playIt(soundFile){
    	
    	//capture the element that's currently focused:	
    	focusedElement = document.activeElement;
    	
    	// Set other variables:
    	var e = document.getElementById("embedid");   
    	var clone = e.cloneNode(true);   
    	clone.setAttribute("src", soundFile);   
    	
    	// *This* is the procedure that requires a synchronous callback
    	// Notice that there are two variables in use: e & clone:
    	e.parentNode.replaceChild(clone, e);
    
    	// This is what I want to run AFTER the
    	// "e.parentNode.replaceChild(clone, e);" line above
    	// Notice that it uses one variable: focusedElement:
    	if (focusedElement != null) {focusedElement.focus();}
    
    	// This line conceivably could directly come after the
    	// "e.parentNode.replaceChild(clone, e);" line above,
    	// but then the "focusedElemend.focus()" line must follow this line:
    	document.getElementById('embedid').autostart='true';
    	
    	
    }
    Many thanks,
    ~~Tom

  2. #2
    Join Date
    Apr 2006
    Location
    Perth
    Posts
    154
    Why is setting the focus a necessity?

    Replacing an entire object seems like overkill. Specifying the object's autostart
    attribute be set to true indicates the object can be communicated with. Why
    not try:

    document.getElementById('embedid').source="newsource.mp3";

    Another simplistic method, if applicable, would be to embed as many objects as
    necessary with only the current visible. Set all objects (or their parent) to
    display=none and then set the required object to display=block.

    If you have only one of these embedded objects, does getElementsByTagName
    return a valid handle to a replaced object in the zeroth array element?

  3. #3
    Join Date
    Aug 2011
    Posts
    12
    It's for a routine that calls audio files dynamically that will be played in the background; it can't be predicted in advance which files will be needed. Once a page is loaded, you CANNOT change the source of the embed to point at another file. It may *look* like you can, because you can use getElementById("embedId").setAttribute.src="new.mp3" etc., and if you subsequently check programmatically to see what the src is, it will *show* "new.mp3", but in reality when you play the embeded element, it will play the original embedded file, not the new embedded file. The only way to do this (that I've found, at least) is with the method used above.

    But, as my post explains, the asynchronous call to replaceChild finishes *after* the other code has finished, and it (replaceChild) wipes away the current focus (which will be one of several different buttons in the application). Thus I need to capture the current focus before the asynchronous call to replaceChild and then restore that focus afterwards.

    So, I need to determine how to make the focus() routine a callback to the replaceChild routine. Do you know how to do that? My experiments are not succeeding, but I've never crafted a callback before...

  4. #4
    Join Date
    Jul 2008
    Location
    urbana, il
    Posts
    2,787
    replaceChild does not accept a callback, so you need to use events.
    if you are correct that you lose focus, it sounds like the blur() event fires when you need to have code fire, so try something like this:

    Code:
    function playIt(soundFile){
    	
    	//capture the element that's currently focused:	
    	focusedElement = document.activeElement;
    	
    	// Set other variables:
    	var e = document.getElementById("embedid");   
    	var clone = e.cloneNode(true);   
    	clone.setAttribute("src", soundFile);   
    	
            focusedElement.onblur=function(){
    
               focusedElement.onblur=null;
    	  // This line conceivably could directly come after the
    	  // "e.parentNode.replaceChild(clone, e);" line above,
    	  // but then the "focusedElemend.focus()" line must follow this line:
    	  document.getElementById('embedid').autostart='true';	// This is what I want to run AFTER the
    	  // "e.parentNode.replaceChild(clone, e);" line above
    	  // Notice that it uses one variable: focusedElement:
    	  if (focusedElement != null) {focusedElement.focus();}
    
            };//end onblur();
    
    	// *This* is the procedure that requires a synchronous callback
    	// Notice that there are two variables in use: e & clone:
    	e.parentNode.replaceChild(clone, e);
    
    
    	
    }
    changes in green


    i think you might be wanting to do clone.focus() instead of focusedElement.focus(), but i'm not 100% sure what you're meaning to do here.
    is this an <embed> tag? consider using <audio> instead. you can reload many files into the same tag, and it's got a cross-platform api.
    <embed> is old and quirky, though it does work (somewhat) on the big two browsers...
    Last edited by rnd me; 08-21-2011 at 12:23 AM.

  5. #5
    Join Date
    Apr 2006
    Location
    Perth
    Posts
    154
    I agree with RND ME's last comment. Find something other than embed. I was never
    fully accepted as a valid HTML tag

    FF wanted an apple quicktime pluging while IE7 was doing fine with media player. after
    installing quicktime FF still wants a plugin and IE7, even though the plugin is loaded,
    does not function at all.

    And, as others have recently said, is focus is all your are concerned about, turn off the
    blur event or set onblur events to trigger a focus() function.

    Oh, but you said you lost the handle to the object...

    I still think searching through the array of getElementsByTagName("EMBED") until the
    correct one is found is a possibility. Search by ID or NAME, or even simply forcing the
    issue with getElementsById("EMBED")[0] and setting focus to it.

    You've indicated that the sequence of events has some importance. If there is an issue
    with creating a new embed and attempting access to it before the browser has fully
    integrated it into the DOM, perhaps leave off focus for a second with a timeout.

  6. #6
    Join Date
    Aug 2011
    Posts
    12
    I'm afraid you guys aren't getting the problem here. There are several things that at issue:

    1) You can't change the SRC of an embedded object after the page has been loaded using setAttribute() or element.source= , etc. If you do, and you then display the result by writing to the document (or to an alert or prompt), it will show that the change has been made, but in reality it has not: behind the scenes, the SRC is still whatever was indicated when the page was loaded. I challenge you to provide a short HTML script that contradicts this, which you've run yourself and know to be accurate.

    2) You CAN change the SRC of an embedded object using the multi-step process I've employed in my code. It's arcane, but it works.

    3) Because JavaScript is asynchronous, certain functions may eclipse other functions if they do not complete before those other functions are processed. Such is the case with the replaceChild() function, which refreshes the page after it's completed, thus obliterating (blurring) any focus on any element on the page.

    4) I'd love to use <audio> in place of <embed>, but <audio> has limitations on which audio formats can be used in different browsers--see: http://www.w3schools.com/html5/html5_audio.asp. Likewise, I'd love to use <object> in place of <embed>, but I can't find a way to make it invoke a player invisibly or play on demand. If you can supply a short HTML routine that will load a player invisibly and dynamically load an audio file using <audio> or <object>, I'd be very interested.

    Here is a short HTML page that demonstrates the issue I'm dealing with. All you need to run it are two WAV files, "hello.wav" and "goodbye.wav". Anything will do...just create two short WAV files and copy them to the directory where you execute this code and you should be good to go.

    ONE CAVEAT: This code works in IE exclusively. I have another version that will employ a slightly different routine if FireFox or another browser is being used to read the script, but for sake of brevity I'm sending this version, which is less confusing to read and to the point.

    Code:
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    
    <head>
    
    	<title>SOUND TEST 6</title>
    	<meta http-equiv="content-type" content="text/html;charset=utf-8" />		
    	<script type="text/javascript">
    	
    	
    	function playIt(soundFile){		
    	
    		// save the button depressed
    		var focusedElement = document.activeElement;
    			
    		var e = document.getElementById("embedid");   
    		var clone = e.cloneNode(true);   
    		clone.setAttribute("src", soundFile);
    				
    		// This is an asychronous function that isn't finished until
    		// after the rest of the code is read and acted on.
    		// It clears any focus that may be in force when it finishes,
    		// thus undoing the focus() command below, which will have already
    		// been processed:
    		e.parentNode.replaceChild(clone, e); 
    		
    		document.getElementById("embedid").autostart="true";
    		
    		// refocus on the depressed button
    		// (this happens BEFORE the replaceChild() routine above is finished,
    		// which means IT ULTIMATELY HAS NO EFFECT, which is why I need a callback routine.)
    		focusedElement.focus();
    		
    	}
    		
    	</script>
    </head>
    
    <body>
    
    <embed src="" id="embedid" autostart="false" type="audio/wav" hidden="true"/>
    <input id="buttonID1" type="button" value="Hello" onclick="playIt('hello.wav');"/><br />
    <input id="buttonID2" type="button" value="Goodbye" onclick="playIt('goodbye.wav');"/><br />
    
    </body>
    
    </html>
    Many thanks!
    Last edited by CoffeeAdventure; 08-22-2011 at 11:54 AM.

  7. #7
    Join Date
    Apr 2006
    Location
    Perth
    Posts
    154
    Ah ha, you wanted to keep focus on the clicked button. That is only now clear to me.
    Usually I attempt to answer a question explicitly, solving the problem as it is presented;
    not offering a totally different method except when the problematic code is disastrously
    bad. In this instance I need to offer a totally different approach.

    I have tested this in IE and it works perfectly. My FireFox still refuses to install a plugin
    which is compatible with <embed> and I don't have much want to fix the problem
    because I hate websites which make sounds.

    My version replaces the entire content of a DIV. For me this does not appear to change
    the currently focused object.

    Code:
    <html>
     <script type="text/javascript">
      function playIt(file)
       { html=' <embed src="'+file+'" id="embedid" autostart="true" type="audio/wav" hidden="true"/>';
         document.getElementById("aud").innerHTML=html;
       };
     </script>
    <body>
    
    <div id="aud">
     <embed src="" id="embedid" autostart="false" type="audio/wav" hidden="true"/>
    </div>
    
    <input id="buttonID1" type="button" value="Hello" onclick="playIt('hello.wav');"/><br />
    <input id="buttonID2" type="button" value="Goodbye" onclick="playIt('goodbye.wav');"/><br />
    
    </body>
    </html>
    Colin Fiat Simple Solutions To Complex Problems

  8. #8
    Join Date
    Aug 2011
    Posts
    12
    Hmmm...I lose focus when I run your routine. Specifically, as I have it here. To be doubly sure we're on the same page, run it once, as here, clicking the two buttons, then REM-out the two statements inside the playIt() function (by preceding each statement with //), and run it again. When the lines are remmed-out, thin dashed lines outline each button when depressed and then remain when released. Not so when the statements in the playIt() function are un-REMmed...as soon as the audio file plays, the button loses focus and the thin dashed lines disappear. At least, that's what happens in my copy of IE (version 8, running on XP SP3):


    Code:
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    
    <head>
    
        <title>SOUND TEST 9</title>
        <meta http-equiv="content-type" content="text/html;charset=utf-8" />        
        
    	<script type="text/javascript">	
    		function playIt(file) {
    			html=' <embed src="'+file+'" id="embedid" autostart="true" type="audio/wav" hidden="true"/>';
    			document.getElementById("aud").innerHTML=html;
    		}
    	</script>
    	
    </head>
    
    
    
    <body> 
    
    <div id="aud">
    	<embed src="" id="embedid" autostart="false" type="audio/wav" hidden="true"/>
    </div>
    
    <input id="buttonID1" type="button" value="Hello" onclick="playIt('hello.wav');"/><br />
    <input id="buttonID2" type="button" value="Goodbye" onclick="playIt('goodbye.wav');"/><br />
    
    </body>
    </html>

  9. #9
    Join Date
    Apr 2006
    Location
    Perth
    Posts
    154
    I do not lose focus. I can press SPACE repeatedly and use arrow navigation from one to
    the other without problem.

    IE8? They made IE9 because IE8 is bad. Crashed my XP SP3 when I tried it. And when
    the OS was not crashing IE8 crashed. (Or outlook for some unknown reason).

    Also, what plugin do you have? My IE7 comes up with windows media player plug in.
    When I installed quicktime plugins nothing worked.

    Anyway, the problem of losing handle to the button clicked has been resolved. Now you
    can go back and capture which one was clicked and reset the focus. I thought of an
    automated method.

    Make a global variable which is set to the name of the button clicked.
    An inline onBlur="" event handler could say something like:

    onblur="if(focusStr==this.name) this.focus();"

    ...or something like that.
    Colin Fiat Simple Solutions To Complex Problems

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
HTML5 Development Center



Recent Articles