I'm inserting some basic javascript into a long running stored process to generate a "Please Wait" message that should appear at the beginning of a long-running stored process and then be hidden at the end, using a method from Don Henderson: http://www.sascommunity.org/wiki/Generating_Descriptive_Please_Wait_Messages_for_Long_Running_Stored...
The approach has been working fine for me on a 9.3 server, but I'm now migrating it to a 9.4 server, and the "please wait" message is appearing, but is not being hiddden after results are generated. The stored process generates streaming results, and is being consumed in Internet Explorer and Chrome.
Here's sample code:
%stpbegin()
data _pleasewait;
length msg $75;
msg = "Please Wait"; output;
msg = "<img src=""/SASStoredProcess/images/progress.gif"">"; output;
run;
ods html text = "<span id=""pleaseWait"">";
proc report data = _pleaseWait nowd;
columns msg;
define msg / " ";
run;
ods html text = "</span>";
data _null_;
x=sleep(3000); *sleep 3 seconds;
run;
proc print data=sashelp.class;
run;
ods html text = "<script>pleaseWait.style.display = ""none""</script>";
%stpend()
I compared the two html files, and other than font styling differences, the main difference I see is that in 9.3 the ods html text appears inside DIV tags, and in 9.4 it appears inside of table tags.
So in 9.3 at the end I get:
<div class="l usertext"><script>pleaseWait.style.display = "none"</script></div>
And in 9.4 I get:
<td class="l usertext"><script>pleaseWait.style.display = "none"</script></td>
Would be grateful for suggestions on how to get this working in 9.4.
Yeah you're right on. This is what I meant when I suggested I'd probably modify the stpbegin and stpend macros, so that you could maybe have the span as the first element of the body, and the script as the last bit of it. The thing is, it kinda annoyed me after I posted it, because I changed my mind on whether it'd be doable.
I had a bit more of a look at it and it'd be hacky and virtually impossible to make it all work with decent html, because of having no way to interrupt the ods stream before the </html> tag to inject the code. You could maybe change the ods html config in %STPBEGIN to include an option for NO_BOTTOM_MATTER and then code up the closing statements in %STPEND yourself, but like I say, hacky.
You could try this with your original code. Not great but it's as standards-friendly as I can think of:
%stpbegin()
data _pleasewait;
length msg $100;
msg = '<div id="pleaseWait">Please Wait<br><img src="/SASStoredProcess/images/progress.gif"></div>';
output;
run;
proc report data = _pleaseWait nowd;
columns msg;
define msg / " ";
run;
data _null_;
x=sleep(3000); *sleep 3 seconds;
run;
proc print data=sashelp.class;
run;
ods html text='</td></tr></table><script>pleaseWait.parentNode.style.display = "none"</script><table><tr><td>';
%stpend()
I'm still not a big fan of it 😕
Found this post from @Eva, who also had problems migrating to 9.4 caused by the change to have the ODS TEXT statement write html text inside of tables instead of <DIV> tags. : https://communities.sas.com/t5/ODS-and-Base-Reporting/ods-html-text-in-SAS-9-4/td-p/224909
From the responses there, it looks like there is no easy way to get 9.4 to revert to writing text in <DIV> tags . (Unless you found one, Eva?) Maybe it wouldn't be that hard to muck with the tagset? (I can't remember if there is an editable tagset in 9.4, or if this was essentially locked down by the switch to Lua?)
For those who know about javascript, does it make sense that having the javascript code appear inside of tables would break it ?
Perhaps instead of using ODS HTML statements, I should use try adding PREHTML and POSTHTML to the style template, as in: http://support.sas.com/resources/papers/proceedings14/1673-2014.pdf and see if that avoids the <table> tags.
I added my solution to the original post. Please see there.
Thanks @Eva. Unfortunately, I couldn't get the data _null_ PUT statement approach to work. It still put the values into HTML tables. Titles and footnotes also went into tables.
But I was able to get it working by using the new (I think) PROC ODSTEXT.
Here's my proof-of-concept:
%stpbegin()
*Create PleaseWait message;
proc odstext;
p "<span id=""pleaseWait"">
Please Wait <p>
<img src=""/SASStoredProcess/images/progress.gif"">
</span>
"
;
run;
*Main part of STP;
data _null_;
x=sleep(3000); *sleep 3 seconds;
run;
proc print data=sashelp.class;
run;
*Hide the Pleasewait message;
proc odstext;
p "<script>pleaseWait.style.display = ""none""</script>";
run;
%stpend()
I think something like that will work for my needs (will wrap in a %PleaseWait macro, as in Don's post). But happy to hear of any other solutions.
Hey Quentin,
This is one possible approach:
data _null_;
file _webout;
put '<span id="pleaseWait" style="display:table;margin:0 auto;text-align: center;">';
put "Please Wait<br>";
put '<img src="/SASStoredProcess/images/progress.gif">';
put "</span>";
run;
%stpbegin()
data _null_;
x=sleep(3000); *sleep 3 seconds;
run;
proc print data=sashelp.class;
run;
%stpend()
data _null_;
file _webout;
put '<script>pleaseWait.style.display = "none"</script>';
run;
As you know, you could also modify the stpbegin/stpend macros to insert this code, and apply it universally. Or, you could stream in the full contents of an external file with a CSS/JS based spinner, and make it all a lot prettier using SVG spinners etc. Or, as you say, you could use PREHTML and POSTHTML. I'd avoid that though, as you'd have to do it every single time.
Personally I'd clone my stpbegin/stpend macros and modify them to blanket include an external file with a nicer customised corporate logo SVG/javascript based spinner. %stpbegin_spinner and %stpend_spinner. Oh the possibilities are endless... 🙂
Having <script> tags inside other HTML content isn't great practice.
Nik
Thanks @boemskats. Was expecting to hear from you, and you came through yet again. : )
Great that this keeps the scripting completely outside of the HTML body. That should keep me from mucking up any of my output.
Hi again @boemskats.
I like that your approach works, but is this really valid html, to put a <span> tag before even the <html>, and the script after </html> ?
I get html like:
<span id="pleaseWait" style="display:table;margin:0 auto;text-align: center;">
Please Wait<br>
<img src="/SASStoredProcess/images/progress.gif">
</span>
<html>
<head>
</head>
<body>
</body>
</html>
<script>pleaseWait.style.display = "none"</script>
And from a bit of googling (e.g. http://stackoverflow.com/questions/3037725/is-it-wrong-to-place-the-script-tag-after-the-body-tag), looks like folks generally agree that that script should be just before the closing </body> tags, and having the <span> etc before the opening <html> is probably invalid also, right?
That said, it works (at least in IE), and I've been known to generate invalid HTML in the past, so if you think it's likely this will work for a reasonable amount of time before breaking again, I'll probably go with it it. : )
Yeah you're right on. This is what I meant when I suggested I'd probably modify the stpbegin and stpend macros, so that you could maybe have the span as the first element of the body, and the script as the last bit of it. The thing is, it kinda annoyed me after I posted it, because I changed my mind on whether it'd be doable.
I had a bit more of a look at it and it'd be hacky and virtually impossible to make it all work with decent html, because of having no way to interrupt the ods stream before the </html> tag to inject the code. You could maybe change the ods html config in %STPBEGIN to include an option for NO_BOTTOM_MATTER and then code up the closing statements in %STPEND yourself, but like I say, hacky.
You could try this with your original code. Not great but it's as standards-friendly as I can think of:
%stpbegin()
data _pleasewait;
length msg $100;
msg = '<div id="pleaseWait">Please Wait<br><img src="/SASStoredProcess/images/progress.gif"></div>';
output;
run;
proc report data = _pleaseWait nowd;
columns msg;
define msg / " ";
run;
data _null_;
x=sleep(3000); *sleep 3 seconds;
run;
proc print data=sashelp.class;
run;
ods html text='</td></tr></table><script>pleaseWait.parentNode.style.display = "none"</script><table><tr><td>';
%stpend()
I'm still not a big fan of it 😕
Thanks again Nik,
ods html text='</td></tr></table><script>pleaseWait.parentNode.style.display = "none"</script><table><tr><td>';
That looks like a good way to avoid the <td> and <tr> tags that are being generated. Not pretty, but should get the job done. And all of this unprettiness will at least be hidden in a macro of one sort or the other, so I can comment it there and not look at again until the next time it breaks. : )
Thans again. See you in Vegas.
(Fair warning: 50% chance I'll start another thread today or tomorrow, about another thing you helped me with a couple years ago, that I'm also struggling a bit to get working like I want in 9.4... )
You're welcome Quentin. Where's this other thread though? You can't say that and then just not post it...
Hi Nik,
Well I said 50% chance of posting my second issue, right? I figured it out, but happy to share my dumb mistake. It was one of those debugging sessions which ends with a big self-imposed smack to the head...
As background, the STP I was testing on 9.4 generated the please wait message and then generated an <iframe> used to call a second stored process that actually returned results. This is my implementation of the approach you helpfully described here: https://communities.sas.com/t5/SAS-Stored-Processes/Stored-process-writing-to-the-browser-and-excelx...
On 9.4 I had two problems: the javascript to hide the please wait message after completion wasn't working; and the iframe had the wrong height (5 cm instead of 100%).
This thread resolved the javascript problem. In 9.4 the ODS HTML TEXT ended up in a table, and broke my javascript. The next day I came back to debugging the height, and my process was:
In my MakeIframe test STP where the height worked, I generated the iframe with a simple data _null_ step and PUT statements. In my original STP where the height did not work, I generated the html code for the iframe with, of course, an ODS HTML TEXT statement. So in 9.4 the iframe is generated inside of a <table>, causing it to get a shortened height (I assume inherited from the table). When it's not in a <table> the iframe height of 100% works fine.
So despite the fact that these two problems had the exact same root cause, I stil managed to spend as much time debugging the height issue as I did the javascript issue. All because on step 1 above, my brain didn't look at ODS HTML TEXT="<iframe>....</iframe>"; and think "gee, is there anything I just learned today about ODS HTML TEXT in 9.4?"
On the plus side, it was a good reminder that one of the benefits of online communities is that when you take the time to make a good minimal test case to post as a question, often that test case illuminates the problem enough to provide a solution. And a self-slap to the head.
These things happen, I just don't normally admit to them so publicly!
When you've got some I recommend you spend it getting to grips with the Inspector bit of the developer console of one of the better browsers (Chrome/FF, I prefer the Chrome one). It'll help you wrap your head around a lot of the layout/elements and make this kind of debugging much easier, as you can test your layouts just by editing the css attributes of various elements on the fly...
Registration is now open for SAS Innovate 2025 , our biggest and most exciting global event of the year! Join us in Orlando, FL, May 6-9.
Sign up by Dec. 31 to get the 2024 rate of just $495.
Register now!
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.
Ready to level-up your skills? Choose your own adventure.