/    Sign up×
Community /Pin to ProfileBookmark

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’;

}[/CODE]

Many thanks,
~~Tom

to post a comment
JavaScript

8 Comments(s)

Copy linkTweet thisAlerts:
@DracoMerestAug 21.2011 — 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?
Copy linkTweet thisAlerts:
@CoffeeAdventureauthorAug 21.2011 — 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...
Copy linkTweet thisAlerts:
@rnd_meAug 21.2011 — 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);

[COLOR="SeaGreen"] focusedElement.onblur=function(){

focusedElement.onblur=null;[/COLOR]
// 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();}

[COLOR="SeaGreen"] };//end onblur();[/COLOR]

// *This* is the procedure that requires a synchronous callback
// Notice that there are two variables in use: e & clone:
e.parentNode.replaceChild(clone, e);



}[/CODE]


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...
Copy linkTweet thisAlerts:
@DracoMerestAug 21.2011 — 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.
Copy linkTweet thisAlerts:
@CoffeeAdventureauthorAug 22.2011 — 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>[/CODE]


Many thanks!
Copy linkTweet thisAlerts:
@DracoMerestAug 23.2011 — 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.

&lt;html&gt;
&lt;script type="text/javascript"&gt;
function playIt(file)
{ html=' &lt;embed src="'+file+'" id="embedid" autostart="true" type="audio/wav" hidden="true"/&gt;';
document.getElementById("aud").innerHTML=html;
};
&lt;/script&gt;
&lt;body&gt;

&lt;div id="aud"&gt;
&lt;embed src="" id="embedid" autostart="false" type="audio/wav" hidden="true"/&gt;
&lt;/div&gt;

&lt;input id="buttonID1" type="button" value="Hello" onclick="playIt('hello.wav');"/&gt;&lt;br /&gt;
&lt;input id="buttonID2" type="button" value="Goodbye" onclick="playIt('goodbye.wav');"/&gt;&lt;br /&gt;

&lt;/body&gt;
&lt;/html&gt;
Copy linkTweet thisAlerts:
@CoffeeAdventureauthorAug 23.2011 — 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>[/CODE]
Copy linkTweet thisAlerts:
@DracoMerestAug 23.2011 — 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.
×

Success!

Help @CoffeeAdventure spread the word by sharing this article on Twitter...

Tweet This
Sign in
Forgot password?
Sign in with TwitchSign in with GithubCreate Account
about: ({
version: 0.1.9 BETA 5.19,
whats_new: community page,
up_next: more Davinci•003 tasks,
coming_soon: events calendar,
social: @webDeveloperHQ
});

legal: ({
terms: of use,
privacy: policy
});
changelog: (
version: 0.1.9,
notes: added community page

version: 0.1.8,
notes: added Davinci•003

version: 0.1.7,
notes: upvote answers to bounties

version: 0.1.6,
notes: article editor refresh
)...
recent_tips: (
tipper: @AriseFacilitySolutions09,
tipped: article
amount: 1000 SATS,

tipper: @Yussuf4331,
tipped: article
amount: 1000 SATS,

tipper: @darkwebsites540,
tipped: article
amount: 10 SATS,
)...