Hide document body on page load - javascript

I have a javascript that is loaded in the document head that needs to hide the document body until all page contents have loaded. I have tried:
$(document.body).hide();
But there still appears to be a flicker in some browsers. I am using the latest jQuery library (1.6.2). Firing the event on domready does hide the document body, but causing a flicker as well.
All that I can control is what is in the javascript. I cannot manipulate the static html pages as the script is being developed as a plugin.

Hiding content until the page is loaded is an anti-usability feature. Some parts of the content may take while to load, meanwhile your visitors see nothing. Browsers render content as it is received because users chose that as the preferred model in the very begining.
If you persist with this approach, you must hide the content using script. Otherwise, users with javascript disabled or not available, or where the script fails to execute correctly, will never see the content.
The simplest way to hide content using script is to use document.write to create a style sheet, then remove it to show the content:
document.write( '<style class="hideStuff" ' +
'type="text/css">body {display:none;}<\/style>');
window.onload = function() {
setTimeout(
function(){
var s, styles = document.getElementsByTagName('style');
var i = styles.length;
while (i--) {
s = styles[i];
if (s.className == 'hideStuff') {
s.parentNode.removeChild(s);
return;
}
}
}, 1000); // debug pause
}

The best way to do this is to put all content in a container div and have a style sheet that hides it by default. You can then show the content once everything is loaded. There is no way to run Javascript before the default page content renders so the only way to start out hidden is with a statically defined CSS rule:
HTML:
<body>
<div id="container">
all dynamic page content goes here
</div>
</body>
CSS in a stylesheet with the page to make it initially not visible:
#container {display: none;}
And, then you can do whatever you want with javascript and when you're done building the page, you do this to make it visible:
document.getElementById("container").style.display = "block";
or in jQuery:
$("#container").show();

You can use CSS and JS:
In the top of your document, below the TITLE use CSS:
<style type="text/css">body {visibility: hidden;}</style>
And after this use JS to restore the visibility:
<script type="text/JavaScript">
document.write('<style type="text/css">body {visibility: visible;}</style>');
</script>
Enjoy :)

Related

Print function only works after second click

I have this function to print a DIV.
Whenever the page is loaded and I click in a "Print" link I have, the DIV is shown to be printed without CSS.
If I close Chrome's print visualization page and click in the "Print" link again, the DIV has CSS applied.
Any ideas why?
Javascript
function printDiv(divId) {
var printDivCSSpre =
'<link href="/static/assets/vendor/sb-admin-2-1.0.7/bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">' +
'<link href="/static/assets/vendor/sb-admin-2-1.0.7/dist/css/sb-admin-2.css" rel="stylesheet">' +
'<div style="width:1000px; padding-right:20px;">';
var printDivCSSpost = '</div>';
$('body').append('<iframe id="print_frame" name="print_frame" width="0" height="0" frameborder="0" src="about:blank"></iframe>');
$("link").clone().appendTo($("#print_frame").contents().find("head"));
window.frames["print_frame"].document.body.innerHTML =
printDivCSSpre + document.getElementById(divId).innerHTML + printDivCSSpost;
window.frames["print_frame"].window.focus();
var windowInstance = window.frames["print_frame"].window;
windowInstance.print();
}
HTML
<a id="print" href="#">
<i class="fa fa-print"></i> Print
</a>
<script>
$('#print').click(function () {
printDiv('report')
})
</script>
<div id="report" class="report">
<p># Generated Table#</p>
</div>
First click:
http://imgur.com/a/Go81Y
Closing the print preview page and clicking again in print
http://imgur.com/a/SCxJF
This happens because when you call your printDiv() function, css is also written using inner HTML and in this scenario CSS is not applied during first click because you wrote CSS to the elements even when they do not exist inside DIV.
The function to work as desired has to write DIV contents first and then CSS should be applied. I would say write css after contents of DIV or load on top of your HTML page and just write DIV contents.
Hope that helps.
Every thing is right just change the sequence. In browser debugger on first click it didn't show 'print_frame' in sources section while in second click it does (I am using chrome devtool).
So load in memory frame with css attributes during onload:
var windowInstance;
$(function(){
$('body').append('<iframe id="print_frame" name="print_frame" width="0" height="0" frameborder="0" src="about:blank"></iframe>');
$("link").clone().appendTo($("#print_frame").contents().find("head"));
windowInstance = window.frames["print_frame"].window;
});
and onClick just append html
$('#print').click(function () {
var divId = 'report';
var printDivCSSpre ='<div id="printReportDiv" style="width:1000px; padding-right:20px;">';
var printDivCSSpost = '</div>';
window.frames["print_frame"].document.body.innerHTML = printDivCSSpre + document.getElementById(divId).innerHTML + printDivCSSpost;
window.frames["print_frame"].window.focus();
windowInstance.print();
});
updated jsfiddle
Try this one. The problem mainly arises because the css has not been applied to the page when the print command is initiated. setTimeout is one way to solve it as others have mentioned but it is really not possible to predict how much delay you will need. Slow internet connections will require high delays before you fire the print statement. The following code, however, only fires the print event after the css has been properly applied to the iframe.
$('#print').click(function () {
if($("#print_frame").length == 0) {
$('#report').after('<iframe id="print_frame" name="print_frame" width="0" height="0" frameborder="0" src="about:blank"></iframe>');
}
var $head = $("#print_frame").contents().find("head");
// for now for ease I will just empty head
// ideally you would want to check if this is not empty
// append css only if empty
$head.empty();
$.ajax({
url : "https://dl.dropboxusercontent.com/u/7760475/reports.css",
dataType: "text",
success : function (reports) {
// grab css and apply its content to the iframe document
$head.append('<style>'+reports+'</style>');
$.ajax({
url : "https://dl.dropboxusercontent.com/u/7760475/bootstrap.css",
dataType: "text",
success : function (bootstrap) {
// grab another css and apply its content to the iframe document
// there may be better ways to load both css files at once but this works fine too
$head.append('<style>'+bootstrap+'</style>');
// css has been applied
// clone your div and print
var $body = $("#print_frame").contents().find('body');
// empty for ease
// but later append content only if empty
$body.empty();
$("#report").clone().appendTo($body);
$('#print_frame').get(0).contentWindow.print();
}
});
}
});
});
Use inline CSS instead.
Reason: When we PRINT or save as PDF if fails to fetch external css Files, So we have to use Inline css.
edited your file please see: jsfiddle.net/ytzcwykz/18/
As other people mentioned it is hard to see your problem without seeing the working example of a problem, but just guessing from the code:
Browser is not able to load the CSS before your print() call.
Browser is not able to render the CSS before your print() call.
Keeping that in mind changing your JS function that way might do the trick
function printDiv(divId) {
$("link").clone().appendTo($("#print_frame").contents().find("head"));
window.frames["print_frame"].document.body.innerHTML =
printDivCSSpre + document.getElementById(divId).innerHTML + printDivCSSpost;
window.frames["print_frame"].window.focus();
var windowInstance = window.frames["print_frame"].window;
setTimeout(function() {
windowInstance.print();
}, 0);
}
The idea behind this function is to let browser execute it's code after we added changed the HTML/CSS code in the window - see Why is setTimeout(fn, 0) sometimes useful?
WARNING: this approach is not tested for your particular problem, and it might also not work because we escape/leave the mouse-click call-stack, calling print() method might be not possible out of user-interaction stack.
UPDATE: after looking in the posted jsfiddle - my assumption was correct, the browser needs some time to load and render the CSS, that is why calling the print() right after changing iframe contents doesn't give the desired result. There are 3.5 ways to solve that:
Use events to identify when iframe's document and window has finished loading and rendering. I tried two approaches, and failed so far, need to read docs more carefully about when document and window are behiving during the loading sequence:
we can do that from outside of iframe, i.e. listen to events of iframe element and it's children
we can do that from inside of iframe, i.e. add little javascript snippet inside which will send a message to the parent window when loading is done.
Consider forming the print result different, how about print style-sheets? I.e. add one more style sheet with print-media query to the parent doc and just call print on it?
Consider forming an iframe which is already loaded and ready to be printed, but replace just the table contents inside it.
As others mentioned, The problem here is that the CSS files used are external resources and browser takes time to download and cache it locally. Once it is cached, it would serve faster and that's why it works fine from the second click.
As Anton mentioned, setTimeout is the key here! You may probably increase the timeout seconds to make that work. I tried setting it to 500ms and that worked,
setTimeout(function(){windowInstance.print();},500);

How do I pick which CSS class is displayed on page load using JQuery?

I have an image version and a text version of my site logo in the following HTML tags. I want to pick which one to show based on where in the page I am vertically and the screen width.
<a class="navbar-brand" id="top-logo" href="#top"><img src="display_board.png" alt="Page Title"></a>
<a class="navbar-brand scroll" id="bottom-logo" href="#top">Page Title</a>
The following script functions correctly for screen resizing and scrolls once the page is loaded. When the site is loaded on mobile, the image logo appears first when viewing on mobile before quickly updating to the text logo, since the script file is loaded at end of the HTML file.
var method_update_nav = function() {
var buffer = 200;
var horizontal_switch_point = 1200;
var vertical_switch_point = $('.carousel-indicators').offset().top - buffer;
var window_top = Math.round($(window).scrollTop());
if ($(window).width() <= horizontal_switch_point || window_top >= vertical_switch_point) {
$('nav').removeClass('top');
$('nav').addClass('bottom');
$('#top-logo').hide();
$('#bottom-logo').show();
} else {
$('nav').removeClass('bottom');
$('nav').addClass('top');
$('#top-logo').show();
$('#bottom-logo').hide();
}
};
var main = function() {
/* UPDATE NAV */
method_update_nav();
$(window).resize(method_update_nav);
$(window).scroll(method_update_nav);
}
$(document).ready(main);
Loading the script in the HTML <header> causes the script to not function, since the classes the JQuery is referencing haven't been defined in HTML tags yet.
Is there a way to pick the correct class to show as the page loads, but after the classes have been defined in the HTML tags?
Loading the script in the HTML <header> causes the script to not function, since the classes the JQuery is referencing haven't been defined in HTML tags yet.
You say it yourself!
So you are using jQuery and I hope you know that you must place the script which references to HTML elements at the bottom of the page in:
$(document).ready(function(){
//script will now get the values because the HTML in now Loaded
})
I advise you to read some tutorials on the web.

How to access to content of iframe (by different domain)? [duplicate]

How can I control the background image and colour of a body element within an iframe? Note, the embedded body element has a class, and the iframe is of a page that is part of my site.
The reason I need this is that my site has a black background assigned to the body, and then a white background assigned to divs that contain text. A WYSIWYG editor uses an iframe to embed content when editing, but it doesn't include the div, so the text is very hard to read.
The body of the iframe when in the editor has a class that isn't used anywhere else, so I'm assuming this was put there so problems like this could be solved. However, when I apply styles to class.body they don't override the styles applied to body. The weird thing is that the styles do appear in Firebug, so I've no idea what's going on!
Thanks
UPDATE - I've tried #mikeq's solution of adding a style to the class that is the body's class. This doesn't work when added to the main page's stylesheet, but it does work when added with Firebug. I'm assuming this is because Firebug is applied to all elements on the page whereas the CSS is not applied within iframes. Does this mean that adding the css after window load with JavaScript would work?
The below only works if the iframe content is from the same parent domain.
The following code works for me. Tested on Chrome and IE8. The inner iframe references a page that is on the same domain as the parent page.
In this particular case, I am hiding an element with a specific class in the inner iframe.
Basically, you just append a style element to the head section of the document loaded in a frame:
frame.addEventListener("load", ev => {
const new_style_element = document.createElement("style");
new_style_element.textContent = ".my-class { display: none; }"
ev.target.contentDocument.head.appendChild(new_style_element);
});
You can also instead of style use a link element, for referencing a stylesheet resource.
An iframe is a 'hole' in your page that displays another web page inside of it. The contents of the iframe is not in any shape or form part of your parent page.
As others have stated, your options are:
give the file that is being loaded in the iframe the necessary CSS
if the file in the iframe is from the same domain as your parent, then you can access the DOM of the document in the iframe from the parent.
You cannot change the style of a page displayed in an iframe unless you have direct access and therefore ownership of the source html and/or css files.
This is to stop XSS (Cross Site Scripting)
This code uses vanilla JavaScript. It creates a new <style> element. It sets the text content of that element to be a string containing the new CSS. And it appends that element directly to the iframe document's head.
Keep in mind, however, that accessing elements of a document loaded from another origin is not permitted (for security reasons) -- contentDocument of the iframe element will evaluate to null when attempted from the browsing context of the page embedding the frame.
var iframe = document.getElementById('the-iframe');
var style = document.createElement('style');
style.textContent =
'body {' +
' background-color: some-color;' +
' background-image: some-image;' +
'}'
;
iframe.contentDocument.head.appendChild(style);
Override another domain iframe CSS
By using part of SimpleSam5's answer, I achieved this with a few of Tawk's chat iframes (their customization interface is fine but I needed further customizations).
In this particular iframe that shows up on mobile devices, I needed to hide the default icon and place one of my background images. I did the following:
Tawk_API.onLoad = function() {
// without a specific API, you may try a similar load function
// perhaps with a setTimeout to ensure the iframe's content is fully loaded
$('#mtawkchat-minified-iframe-element').
contents().find("head").append(
$("<style type='text/css'>"+
"#tawkchat-status-text-container {"+
"background: url(https://example.net/img/my_mobile_bg.png) no-repeat center center blue;"+
"background-size: 100%;"+
"} "+
"#tawkchat-status-icon {display:none} </style>")
);
};
I do not own any Tawk's domain and this worked for me, thus you may do this even if it's not from the same parent domain (despite Jeremy Becker's comment on Sam's answer).
An iframe has another scope, so you can't access it to style or to change its content with javascript.
It's basically "another page".
The only thing you can do is to edit its own CSS, because with your global CSS you can't do anything.
This should work with cross domain if you're the owner of the both
The trick here is to assign a global css variable to your body, to listen message with the new color, and then to change the global css variable once receive a message.
I'm using angular, but it should work with pure javascript
My use case was to show to the user what he how the color change would impact his website in the iframe before saving it
Domain A
#ViewChildren('iframeContainer') iframeContainer: QueryList<ElementRef>
sendDataToIframe(
data = {
type: 'colorChange',
colors: {primary: '#000', secondary: '#fff'},
},
): void {
if (this.targetUrl)
this.iframeContainer.first.nativeElement.contentWindow.postMessage(data) // You may use document.getElementById('iframeContainer') instead
}
Domain B
acceptedEditOrigins = [
'https://my.origine.ccom', // Be sur to have a correct origin, to avoid xss injecto: https://en.wikipedia.org/wiki/Cross-site_scripting
]
constructor() {
// Listen to message
window.addEventListener('message', (event) => this.receiveMessage(event), false)
}
receiveMessage(event: MessageEvent) {
if (this.acceptedEditOrigins.includes(event.origin))
switch (event.data.type) {
case 'colorChange': {
this.setWebsiteConfigColor(event.data.colors)
}
}
}
setWebsiteConfigColor(colors: WebsiteConfigColors) {
if (colors) {
const root = document.documentElement
for (const [key, value] of Object.entries(colors)) {
root.style.setProperty(`--${key}`, value) // --primary: #000, --secondary: #fff
}
}
}
body {
background-color: var(--primary);
}
If you have control of the page hosting the iframe and the page of the iframe, you can pass a query parameter to the iframe...
Here's an example to add a class to the iframe based on whether or not the hosting site is mobile...
Adding iFrame:
var isMobile=$("mobile").length; //detect if page is mobile
var iFrameUrl ="https://myiframesite/?isMobile=" + isMobile;
$(body).append("<div id='wrapper'><iframe src=''></iframe></div>");
$("#wrapper iframe").attr("src", iFrameUrl );
Inside iFrame:
//add mobile class if needed
var url = new URL(window.location.href);
var isMobile = url.searchParams.get("isMobile");
if(isMobile == "1") {
$("body").addClass("mobile");
}
For juste one iframe, you can do something like this:
document.querySelector('iframe').contentDocument.body.style.backgroundColor = '#1e1e2d';
In case you have multiple iframe you're dealing with:
document.querySelectorAll('iframe').forEach((iframe) => {
iframe.contentDocument.body.style.backgroundColor = '#1e1e2d';
});
Perhaps it's changed now, but I have used a separate stylesheet with this element:
.feedEkList iframe
{
max-width: 435px!important;
width: 435px!important;
height: 320px!important;
}
to successfully style embedded youtube iframes...see the blog posts on this page.
give the body of your iframe page an ID (or class if you wish)
<html>
<head></head>
<body id="myId">
</body>
</html>
then, also within the iframe's page, assign a background to that in CSS
#myId {
background-color: white;
}

Modifying body HTML content before DOM is loaded and displayed in browser

I am trying to modify the body of a page before the DOM is displayed using JavaScript or jQuery.
I'm able to do it fairly well with:
jQuery(document).ready(function() {
jQuery('body').load('?variation-id='+dynamic_id_number, function() {});
});
But the problem is that the page renders the old body content for a split second before showing the new body content.
Is there a way I can detect when the DOM is loaded and modify it before it is printed?
If you're waiting for document.onready, it'll wait for the dom to be ready ;). Don't use it, just embed it (your script) in your head tag.
The DOM is fully visible at the same instant it is fully loaded.
If you want to avoid this, I suppose you could just hide the body with CSS and show it again when you're done:
CSS:
body { display: none; }
jQuery:
jQuery(document).ready(function() {
jQuery('body').load('?variation-id='+dynamic_id_number, function(){
jQuery('body').show();
});
});
You can hide BODY element contents or BODY element itself via CSS:
HTML.hidden > BODY {
display: none;
}
hidden class should be added right inside HEAD section (while <body> opening tag is not even encountered by browser yet):
$(document.documentElement).addClass('hidden');
and then unhide it in ready() handler:
$(document).ready(function() {
$(document.documentElement).removeClass('hidden');
});
Hiding BODY element statically is bad for cases when JS execution is disabled.
Instead of using $(document).ready(..), just drop the script directly onto the page, preferably after the element being referenced has been defined. Example:
<div id="foo">Bar</div>
<script>$("#foo").html("Loaded!");</script>
Its not pretty, but it does the job.
If you don't have access to the page's source code, you can try this:
var css = 'body { display:none; background-color:white; }',
style = document.createElement('style');
style.type = 'text/css';
if(style.styleSheet)
{
style.styleSheet.cssText = css;
} else
{
style.appendChild(document.createTextNode(css));
}
head = document.head || document.getElementsByTagName('head')[0],
head.appendChild(style);

Javascript problem with iframe that's hidden before loaded

I have a page that contains an iframe that gets loaded using Javascript:
index.html
<iframe id="myFrame" width="800" height="600" style="display: none;"></iframe>
<div id="loader"><!-- some loading indicator --></div>
<script type="text/javascript">
function someFunction() {
var myFrame = document.getElementById('myFrame');
var loader = document.getElementById('loader');
loader.style.display = 'block';
myFrame.src = 'myFrame.html';
myFrame.onload = function() {
myFrame.style.display = 'block';
loader.style.display = 'none';
};
}
</script>
The page that gets loaded in the iframe contains some Javascript logic which calculates the sizes of certain elements for the purposes of adding a JS driven scrollbar (jScrollPane + jQuery Dimensions).
myFrame.html
<div id="scrollingElement" style="overflow: auto;">
<div id="several"></div>
<div id="child"></div>
<div id="elements"></div>
</div>
<script type="text/javascript">
$(document).load(function() {
$('#scrollingElement').jScrollPane();
});
</script>
This works in Chrome (and probably other Webkit browsers), but fails in Firefox and IE because at the time jScrollPane gets called, all the elements are still invisble and jQuery Dimensions is unable to determine any element's dimensions.
Is there a way to make sure my iframe is visible before $(document).ready(...) gets called? Other than using setTimeout to delay jScrollPane, which is something I definitely want to avoid.
Some browsers assume that when "display:none" is applied to replaced elements (like Flash or an iframe) the visual info for that element is no longer needed. So, if the element is later displayed by the CSS, the browser will actually recreate the visual data form scratch.
I imagine that having the iframe default to "display:none;" makes the browser skip the rendering of the HTML so the tags don't have any dimensions. I would set the visibility to "hidden" or position it off the page rather than use "display:none;".
Good luck.
instead of making the iframe invisible by using display:none, you could try to...
... set visibility:hidden
... set position:absolute; top:-600px;
... set opacity:0
or something else that makes jQuery "see" the objects but not the user (and reset the used css-attributes in your myFrame.onload function).
visibility:collapse;
display:hidden;
height:0px;
Will work to get rid of white space too..
The iframe will also load..
Hidden iframes are a huge security issue. Probably best to try to find another way to accomplish what you want, if it is legitimate, because hopefully future browsers will get rid of this feature altogether. http://blog.opendns.com/2012/07/10/opendns-security-team-blackhole-exploit/

Categories

Resources