We have some legacy HTML content which we must render in compatibility mode. The requirement comes from our customers who want their HTML-based reports (some of which were created back in IE6 days) to look and print exactly the same, regardless of the browser version or underlying technologies. At the same time, we want to use Standard Mode and HTML5 for the rest of our web app.
An obvious solution is to host the legacy content in an <iframe> in compatibility mode. The following appears to work cross-browser:
main.html (in standard mode):
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title></title>
<style type="text/css">
body {
font-family: Arial;
font-size: 9pt;
font-style: italic;
font-weight: bold;
}
</style>
<script type="text/javascript">
window.onload = function () {
info.firstChild.data = "document.compatMode: " + document.compatMode;
// test frame's HTML5 API: document.getSelection()
setInterval(function () {
var selection = document.getElementById("contentFrame").contentDocument.getSelection();
var selectedNode = selection.focusNode;
if (selectedNode)
info2.firstChild.data = "Selected node: " + selectedNode.nodeName + ", offset: " + selection.focusOffset;
else
info2.firstChild.data = "";
}, 500);
}
</script>
</head>
<body>
<h1>Standard Mode Page</h1>
<span>body font</span>
<table border="1">
<tr>
<td>Table font</td>
</tr>
</table>
<span>body font</span>
<pre id="info"> </pre>
<pre id="info2"> </pre>
<iframe id="contentFrame" style="width: 500px; height: 300px;" src="frame.html"></iframe>
</body>
</html>
frame.html (in compatibility mode):
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "">
<html>
<head>
<title></title>
<style type="text/css">
body {
font-family: Arial;
font-size: 9pt;
font-style: italic;
font-weight: bold;
}
</style>
<script type="text/javascript">
window.onload = function () {
info.firstChild.data = "document.compatMode: " + document.compatMode;
editable.focus();
}
</script>
</head>
<body>
<h1>Compatibility Mode Frame</h1>
<span>body font</span>
<table border="1">
<tr>
<td>Table font</td>
</tr>
</table>
<span>body font</span>
<pre id="info"> </pre>
<div id="editable" contentEditable="true" style="border: 1px dotted red">
Editable
</div>
</body>
</html>
Note the difference in rendering the table, using the same CSS:
My question to experienced web developers: is this a supported scenario which can be used in production environment (IE8+ mostly, but occasionally Safari/Chrome/Firefox)? Is there a better way of doing this?
I've stumbled upon a related, albeit opposite question, which left me with mixed feelings.
[UPDATE] (based on the comments):
All JavaScript resides inside the main page and appears to work just fine. What's interesting (and great!), the inner frame's view is rendered in compatibility mode, yet standard mode features are available for its DOM (at least, when accessed from the main page). E.g. document.getSelection works (and does cross-browsers, too).
By supported scenario I mean any endorsement by W3C HTML and DOM standards. So far I haven't found a definitive answer to this. This behavior may as well be just a nice side effect, although the fact it works cross-browsers is promising.
MSDN says the following: As of IE9 mode, webpages cannot display multiple document modes. For example, consider a standards-based webpage that contains a frame element that displays content in quirks mode. IE9 mode displays the child frame in standards mode (because the parent document is in standards mode). According to my tests, this is not true; my sample works as desired in IE9: the main page is in standard mode, the frame page is in quirk mode. [EDITED] As pointed out in the comments, it is the Almost Standard Mode (i.e., not the classic quirk mode), with its own rendering rules.
As of IE9 mode, webpages cannot display multiple document modes. For example, consider a standards-based webpage that contains a frame
element that displays content in quirks mode. IE9 mode displays the
child frame in standards mode (because the parent document is in
standards mode).
According to my tests, this is not true; my sample
works as desired in IE9: the main page is in standard mode, the frame
page is in quirk mode.
Not quite; when your sample works as desired it's actually displayed in a single display mode, with quirks mode emulated for the frame content. You shouldn't care about the underlying mechanics as long as the resulting output matches what you were after, although there is some anecdotal evidence of differences between emulated and native modes (mostly to do with js DOM manipulation).
I'd be a bit more concerned about how IE10+ would handle such fringe scenarios:
Starting with IE11 Preview, document modes are deprecated and should
no longer be used, except on a temporary basis. Make sure to update
sites that rely on legacy features and document modes to reflect
modern standards.
Ninja edit: looks like this has already been resolved on SO; modifying the accepted solution to your needs you should omit Doctype and add <meta http-equiv="X-UA-Compatible" content="IE=5" />; X-UA-Compatibility correctly defined as per msdn spec
This is a pseudo-answer to your slightly non-specific question;
In regards to your apprehension to rely on this IE feature for "back-compatibility", I feel the same way. Microsoft has provided this option because there are lots of companies out there who take a long time to update their web content. This option is meant to allow them to have a quick and dirty stop-gap, not a permanent solution.
So, what's the permanent solution? If that is your question, then IMO this is the answer; don't rely on the stop-gap and develop the correct output for the reports.
Without knowing what those reports are it is impossible to properly advise you on that part, but here's a stab in the dark:
There are lots of options to convert "HTML" to PDF. (I put HTML in quotations because each rendering engine is bound to require different versions/standards of HTML and you'll need to know those assumptions before you pick one.) If you want output that will format 100% the same on any browser, then you want a format that is meant to be static and not change; like PDF. Plus, then you also have the printing options taken care of as well.
Related
I seek to run the following iframe object inside an HTA application and would like to later convert this to an exe file.
<iframe height="620" class="wizard-frame" style="max-width: 100% !important; border: 1px solid #dadada; overflow-y: hidden;" scrolling="no" src="https://www.rnv-online.de/timetable/?design=3&width=390&destination=" width="390"></iframe>
The appearance of the applicaiton should look as displayed here:
https://www.rnv-online.de/fahrtinfo/fahrplaene/fahrplanauskunfts-widget/
Here is the HTML Code I have come up with:
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<html>
<body>
<iframe
height="620"
class="wizard-frame"
style="max-width: 100% !important; border: 1px solid #dadada; overflow-y: hidden;" scrolling="no" src="https://www.rnv-online.de/timetable/?design=3&width=390&destination=" width="390"></iframe>
</body>
</html>
When I run the HTA file, I receive an error, which I can skip and get the asked widget. However, it is not interactive and I assume Javascript is an issue here. How can I get to work properly?
The page in the script does not work in IE 11 (e.g. calendar and clock do not work at all). If the page doesn't work in IE 11, it won't work in an HTA iframe either.
If you can find a page that actually works with IE 11, then the following information may be of use...
Even with the x-ua-compatible line in the HTA, the external site will get the mshta.exe default user agent header which is MSIE 7.0 by default. To ensure the external site gets an IE 11 header, apply this registry setting:
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION]
"mshta.exe"=dword:00002af8
That will change the mshta.exe default mode from IE=7 to IE=11.
While this answer may appear to be a duplicate of other answers that refer to x-ua-compatible and the FEATURE_BROWSER_EMULATION registry value, I could not find any answers that made it clear that the x-ua-compatible setting does not carry over to the iframe. Here's a sample HTA script that demonstrates the issue:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=11">
</head>
<body>
<script>
window.resizeTo(850,650);
alert(document.documentMode + "\n\n" + window.navigator.userAgent);
</script>
<iframe width=800 height=550 src="https://gs.statcounter.com/detect"</iframe>
</body>
</html>
I just tried FMX TWebBrowser in Delphi 10.3.3. I could not rotate a photo in img tag. The following page is working in Google Chrome. But it is not working in FireMonkey TWebBrowser of Delphi 10.3.3. What is wrong? Please someone help me!
<!DOCTYPE html>
<html>
<head>
<style>
img {
display: block;
width: 300px;
height: auto;
}
</style>
</head>
<body>
<button onclick="rotate();">Rotate 90 degrees</button>
<br />
<img src="20190228-1.JPG" id="theID" />
<script>
function rotate() {
var imgX=document.getElementById("theID");
imgX.style.transform = "rotate(90deg)";
imgX.style.display = "block";
}
</script>
</body>
</html>
I guess your target platform is Windows. TWebBrowser wraps IE based web browser control on Windows which displays pages in IE7 standard mode by default. This mode doesn't support CSS transformations. You have multiple options to workaround that.
Option 1: Use deprecated -ms-filter CSS property
-ms-filer or filter is Microsoft CSS extension to apply collection of graphic filters to an object. It also supports rotation:
imgX.style.filter = "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
Option 2: Force Egde standard mode via registry
This is also what TWebBrowser documentation encourages you to do on Windows platform. You basically need to enlist your application's EXE name under HKEY_CURRENT_USER\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION either manually or programmatically as DWORD value that defines compatibility mode for your application. 11001 ($2AF9) is for IE11 edge mode. See Browser Emulation for further values. This setting will affect all pages loaded in any web browser control within your application.
Option 3: Use x-ua-compatible header to specify legacy mode
You should be able to achieve the same effect as in option 2 by injecting x-ua-compatible header via <meta> tag in your HTML:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="x-ua-compatible" content="IE=edge">
…
See Specifying legacy document modes for more information.
All of the above applies to Windows platform. Keep that in mind when picking from the options. Option 1 most likely won't work on other platforms.
While you're at it consider also separating JavaScript from CSS by leveraging CSS classes:
<style>
img {
display: block;
width: 300px;
height: auto;
}
.rotated {
transform: rotate(90deg);
}
</style>
…
<script>
function rotate() {
var imgX = document.getElementById("theID");
imgX.classList.toggle("rotated");
}
</script>
Chrome v75 appears to have introduced a bug whereby if you replace an iFrame's src programatically, it will replace the entire page instead of the iFrame.
This didn't happen on v74 and I can't get a test case to work (yet), it just fails in our site. (The site hasn't changed since going from v74 to v75, only Chrome has changed)
It appears to work fine the first time but then when you change it again (in our case viewing report drill downs) it causes the entire page (i.e. the iFrame's Parent) to load the src you were trying to load into the iFrame.
It also doesn't matter if you use pure Javascript or (in our case) JQuery, both cause the same issue.
EDIT: After a few hours detective work, I've found the bug. Setting the tag in the iFrame's content causes Chrome to load the iFrame's content into it's parent rather than the iFrame itself.
I've setup a Plunker account with a demo: https://plnkr.co/edit/UQ0gBY?plnkr=legacy&p=info
Just so I can post the link to Plunker, here is the code for the main file & the iframe content
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
function onLoaded() {
// find element
let button = document.getElementById("button");
button.addEventListener("click",function(e){
// Add a random number on the end as a cache buster
document.getElementById('frame-finance-custom').src = 'test2.html?rnd=' + Math.random();
},false);
};
document.addEventListener('DOMContentLoaded', onLoaded, false);
</script>
</head>
<body>
<div>IFrame Src Changing Test</div>
<div>
<div id="div-frame-finance-custom" style="float:left;width:33%">
<iframe id="frame-finance-custom" name="frame-finance-custom" class="iframe"
style="border:1px solid black; width: 100%; height: 350px; overflow-y: scroll; vertical-align: top;">
no data
</iframe>
</div>
<div style="float:left;margin-left:1em;">
Detail: Loading an iframe page with a <Base> tag in it with target set to "_parent" will cause any refresh of that frame to replace the parent document<BR>
<BR>Instruction: <UL><LI>Click the 'Update Frame' Button, this will load test2.html into the frame. <LI>Click it again & it will replace the iframe's parent with the content of the iFrame.</UL>
<BR>Confirmation: Remove the <Base> tag from the header of test2.html & reload, it will work as expected.
</div>
</div>
<br clear=both>
<div>
<button id="button">
Update Frame
</button>
</div>
</body>
</html>
IFrame Content (test2.html):
<!DOCTYPE html>
<html lang="en">
<head>
<base target="_parent"/>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>This is the frame content</div>
</body>
</html>
Note, using their new layout it doesn't work, but using their legacy layout it does. Feel free to save the files locally and use chrome directly too.
Ok, so this turned out to be a bug in Chrome rather than anything else, so yes, strictly not a SO question, but seeing as SO ranks so well in Google (other search engines are available), I thought it better to leave it here as a solution rather than simply delete it, just incase anyone else has a similar problem.
The reason is outlined as an edit in my question, the solution is to remove the <base target="_parent"> tag from the iFrame and programatically add the 'target="_parent"' attribute to any links in the iFrame.
We do this via jQuery, I'm sure its just as easy via vanilla Javascript.
$('a').attr('target','_parent');
Add that to the javascript that runs when a page has loaded and it'll replace add target="_parent" to any links on the page.
e.g.
<script>
function onLoaded() {
// find all links and add the target attribute
$('a').attr('target','_parent');
};
document.addEventListener('DOMContentLoaded', onLoaded, false);
</script>
As #Kaiido says in his comment, its apparently fixed in Chrome v77, but this isn't the current (as of June 2019) stable release, so we've had to add the workaround into production so that our CRM works with Chrome v75. Thanks to #Kaiido for confirming that.
I am using the following jquery plugin to enable fullscreen in my web app.
https://github.com/martinaglv/jQuery-FullScreen
It works great, except that when I am in fullscreen mode my textarea is not editable? Why is this? Is it a browser thing or the plugin?
It doesnt seem to be disabled in any way...
My test code below:
<!DOCTYPE html>
<html>
<head>
<title></title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
<script type="text/javascript" src="https://raw.github.com/martinaglv/jQuery-FullScreen/master/fullscreen/jquery.fullscreen.js"></script>
</head>
<body>
<div id="content">
<textarea></textarea>
<button>
Go Go Fullscreen</button>
</div>
<script type="text/javascript">
(function () {
if ($.support.fullscreen) {
$('button').click(function (e) {
$('#content').fullScreen();
});
}
})();
</script>
</body>
</html>
There is a workaround (for Chrome, at least), but I don't know how it factors into the plug-in you're using. You have to pass along a little instruction to allow keyboard input:
document.body.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
UPDATE:
You could modify the plug-in pretty easily. This function would change to reflect the above:
function requestFullScreen(elem){
if (elem.requestFullscreen) {
elem.requestFullscreen();
} else if (elem.mozRequestFullScreen) {
elem.mozRequestFullScreen();
} else if (elem.webkitRequestFullScreen) {
elem.webkitRequestFullScreen();
}
}
By default, the plug-in forces the browser to enter full-screen mode without allowing keyboard input.
More information here from MDN, and the thread where I found some clues.
From the tutorial:
The idea of allowing developers to programatically take up the user screen doesn’t come without serious security implications, which is why keyboard usage is limited. Of course, there are many legitimate uses for keyboard input in full screen, which is going to be addressed in future revisions of the API via some kind of permission prompt.
However, even in its current, limited form, the API still gives us an opportunity to enhance the experience of the end user.
It doesn't even go full screen in some browsers, like IE 9.
http://tutorialzine.com/2012/02/enhance-your-website-fullscreen-api/
Consider the following Html file:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>test</title>
<style type="text/css">
</style>
</head>
<body>
<script type="text/javascript">
alert('' +
'\'test\'[0] = \'' + 'test'[0] + '\'\n' +
'\'test\'.charAt(0) = \'' + 'test'.charAt(0) + '\'\n'
);
</script>
</body>
</html>
When I open this file on my local machine it gives the following output (both in ie8 and Chrome):
'test'[0] = 't'
'test'.charAt(0) = 't'
When I host this file on IIS7.5 I still get the same result in Chrome but ie8 gives the following result:
'test'[0] = 'undefined'
'test'.charAt(0) = 't'
How is this possible?
Jan, just to add to your own answer, the more standards compliant way to enable compatibility view would be to just use a standard doctype at the beginning of your document.
<!DOCTYPE html>
Which is the HTML5 doctype, should put you into standards mode as well.
OK I found out it was due to compatibility view. Internet explorer comes standard (I think, I am not the owner of this laptop) with the option to display all intranet pages in compatibility View. To force it to use ie8 compatibility view I had to add the following meta tag to the head element:
<meta http-equiv="X-UA-Compatible" content="IE=edge" > <!-- IE8 mode -->
You can also turn this option off under 'Page' > 'Compatibility View Settings'
This question has put me in the right direction.
EDIT: I advise web developers to leave this option checked in ie8, because most of your users will have it checked. This way you will catch these errors in the development cycle.
EDIT2: sv_in is right. IE=edge is much cleaner.