I have a list of elements where I want the user to be able to print just the clicked element, and not the whole surrounding page. The elements looks as follows:
<div class="element" id="#element1">Element 1</div>
<div class="element" id="#element2">Element 2</div>
<div class="element" id="#element3">Element 3</div>
To be able to print just the element the user clicks, I use this code to fetch whatever element is clicked:
jQuery(document).ready(function($) {
$( '.element' ).click(function() {
var clickedEl = $(this).attr('id');
printDiv( clickedEl );
return false;
});
});
The id of the clicked element is then passed on to a function, creating a very simple HTML-page for print:
function printDiv(id) {
var printContents = document.getElementById(id).innerHTML;
var printContentsBefore = '<html><head>\
<link rel="stylesheet" href="/css/blabla_print.css" type="text/css" />\
</head><body><table><tr>';
var printContentsAfter = '</tr></table></body></html>';
var originalContents = document.body.innerHTML;
document.body.innerHTML = printContentsBefore + printContents + printContentsAfter;
//document.body.innerHTML = printContents;
window.print();
document.body.innerHTML = originalContents;
}
The code sort of works, but I'm having two problems:
The print window that pops up contains the data of the clicked element about half of the times. The other half the window is empty. How can that be, and how do I solve it?
In the generated page I'm trying to load a CSS-file to format the way the content looks, but without any luck. Is what I'm trying to do here at all possible, and if so how?
And in case it's relevant; the code is being used on a WordPress-based page.
Thanks!
I will never understand why people waste so much effort getting an element's ID, only to then use getElementById to get the element that they already had again...
$(".element").on("click",function() {
printDiv(this);
});
function printDiv(elem) {
var before = '...',
after = '...',
result = before + elem.innerHTML + after;
// now do stuff
}
It's worth noting that window.print() does not necessarily fire immediately. It's not synchronous. It just tells the browser that it should bring up the Print box. Therefore, resetting originalContents back on your content may or may not mess it up, as you have seen.
You may wish to have a "Cancel" button, styled with
#media print {
.cancelbutton {display:none}
}
Which does the actual reset.
Another note is that you are nuking the innerHTML of the page. Any event handlers that you may have will be completely annihilated, except on the body element itself. Just be aware of that.
Personally I would suggest a different method, one that would involve opening a new window/tab with just the content to be printed (you can open a window and document.write to it, for example).
Related
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);
I'm looking for a way to read the source code of a page after it finished loading and inspect the code to see if it contains a specific text.
I found this reference but this only returns the text visible in the page and not the whole HTML code.
For instance, if the html source code is:
<html>
<header>
<header>
<body>
<p> This is a paragraph</a>
<body>
</html>
I want the script to print exactly the same thing.
Your help is appreciated.
I think you are over-complicating this problem. You don't need to "print" the page's HTML or "inspect the code".
In a comment, you said:
Check if page contains an iframe [and] Display a message if the iframe is found
You can just use DOM traversal functions to examine the DOM.
Try something like this:
window.addEventListener('load', function() {
if(document.getElementsByTagName('iframe').length){
console.log('Found an iframe');
}
});
Or with jQuery:
$(function() {
if($('iframe').length){
console.log('Found an iframe');
}
});
That's so simple, you can use this method to run a script after a page is fully loaded window.onload
function load(){
console.log(document.getElementsByTagName('html')[0].innerHTML);
}
window.onload = load;
For further explanations, check this post
Do like this, call this function on load
Fiddle Demo
function printBody() {
// store oiginal content
var originalContents = document.body.innerHTML;
// get the outer html of the document element
document.body.innerText = document.documentElement.outerHTML;
// call window.print if you want it on paper
window.print();
// or put it into an iframe
// var ifr = document.createElement('iframe');
// ifr.src = 'data:text/plain;charset=utf-8,' + encodeURI(document.documentElement.outerHTML);
// document.body.appendChild(iframe);
// a small delay is needed so window.print does not get the original
setTimeout(function(){
document.body.innerHTML = originalContents;
}, 2000);
}
Src: Print <div id=printarea></div> only?
Assuming that by 'print' you don't actually mean to transfer it to a paper copy, you can add some script like:
window.addEventListener('load', function() {
var content = document.documentElement.innerHTML,
pre = document.createElement('pre'),
body = document.body;
pre.innerText = content;
body.insertBefore(pre, body.firstChild);
});
What this does, step by step is:
window.addEventListener('load', function() > Wait for the page to be fully loaded and then execute the function
content = document.documentElement.innerHTML > store the actual page source in the content variable (document.documentElement refers to the 'root'-node, usually <html> in html documents
pre = document.createElement('pre') > create a new <pre>-element
body = document.body > create a reference to the <body> element
pre.innerText = content > assign the HTML-structure we've stored earlier as text to the <pre>-element
body.insertBefore(pre, body.firstChild) > put the <pre>-element (now with contents) before any other element in the body (usually on top of the page).
This leaves you with the entire source (as it was before creating the <pre>-element containing the source) on top of you page.
Edit: Added <iframe> workflow
It was not clear to me you actually wanted to target an <iframe>, so here's how to do that (using a naive approach, more on that further on):
window.addEventListener('load', function() {
var iframeList = document.getElementsByTagName('iframe'),
body = document.body,
content, pre, i;
for (i = 0; i < iframeList.length; ++i) {
content = iframeList[i].documentElement.innerHTML;
pre = document.createElement('pre');
pre.innerText = content;
body.insertBefore(pre, body.firstChild);
}
});
why is this approach naive?
There is a thing called Same-Origin-Policy in javascript, which prevents you from accessing <iframe>-content which if the contents do not originate from the same domain as the page containing the <iframe>.
There are several ways to take this into consideration, you could wrap the inside of the for-loop in try/catch-blocks, though I prefer to use a more subtle approach by not even considering <iframes> which do not match the Same-Origin-Policy.
In order to do this, you can swap the getElementsByTagName method with the querySelectorAll method (please note the compatibility table at the bottom of that page, see if it matches your requirements).
The querySelectorAll accepts a valid CSS selector and will return a NodeList containing all matching elements.
A simple selector to use would be
'iframe[src]:not([src^="//"]):not(src^="http")' which selects all iframe with a src attribute which does not start with either // or http
Disclaimer: I never use a <base>-tag (which changes all relative paths within the HTML) or refer to the current website using a path containing the domain, so the example CSS-selector does not consider these aberrations.
Can you use :not()
IE9 or better
Can you use document.querySelector(All)
IE8 or better (in order to use with :not(), IE9 or better)
hover/click the boxes above to show the spoiler
I'll start by apologising as this may seem like or actually be a duplicate, but I've tried every solution I've encountered and none seem to be working for me.
In my HTML I have an iframe referencing another HTML document. With JavaScript, at the press of a list of buttons I insert text into the body of that iframe. I also use JavaScript to maintain focus on the iframe body. The problem is that nothing appears to work for me to get the cursor to move to the end of the text each time I press those buttons, it always moves to the beginning.
One of the solutions I've tried was to add this code to the function that handles my button presses:
iFrameBody.focus();
var content = iFrameBody.innerHTML;
iFrameBody.innerHTML = content;
so the function looks like this:
function typeIn(buttonId) {
var iFrameBody = document.getElementById("iFrame").contentWindow.document.body;
iFrameBody.innerHTML += buttonId;
iFrameBody.focus();
var content = iFrameBody.innerHTML;
iFrameBody.innerHTML = content;
}
Something else I tried was, in the HTML file referenced by my iframe I did:
<body onfocus="this.innerHTML = this.innerHTML;"></body>
I tried several other more complicated solutions that frankly I didn't even quite understand to be honest, all to no avail. Any help would be much appreciated.
I figured it out. The issue was that I was using a body element, writing to it's innerHTML and trying to set focus on the body. By simply using a textarea inside my iFrame instead it became very simple and it only required the simplest code.
This to set focus when the page loads:
window.onload = function () {
var iFrameTextArea = document.getElementById("iFrame").contentWindow.document.getElementById("iFrameTextArea");
iFrameTextArea.focus();
}
And then this to set the button to write to the textarea while maintaining focus:
function typeIn(buttonId) {
var iFrameTextArea = document.getElementById("iFrame").contentWindow.document.getElementById("iFrameInput");
iFrameTextArea.focus();
iFrameTextArea.value += buttonId;
}
Super easy!!
Instead of again using textarea in iframe, u can also solve this by using the following code.
var iframeElement = document.getElementById("iFrame").contentWindow.document.body;
iframeElement.focus();
var len = iframeElement.length ;
iframeElement.setSelectionRange(len, len);
I'm using a JavaScript print function that print's the predefined div of a page on click. The function is as following:
<head>
<script type="text/javascript">
function printDiv(any printable div) {
var printContents = document.getElementById(any printable div).innerHTML;
var originalContents = document.body.innerHTML;
document.body.innerHTML = printContents;
window.print();
document.body.innerHTML = originalContents;
}
</script>
</head>
The printer form input method and printable div are as following:
<body>
<input type="button" class="button" onclick="printDiv('any printable div')" name="print" value="print" />
<div id = "any printable div"> On button click contents here are printed </div>
</body>
I'm also using a jQuery text marquee / scroller on the same page that performs it task based on the function:
$(document).ready(function(){
All works fine independently. The problem is that the text marquee / scroller stops scrolling the text when the print button is clicked. The scroller can't scroll the text until the page is refreshed. So I'm using html meta refresh but that may be an annoying experience to the visitors. I don't want to use the scroller in an iframe too.
I've tried some jQuery and JavaScript based solution by this time. The scripts refresh (Just fades in and out the whole block of texts) the div where the scroller is silently but actually can't keep the text scrolling.
Is there any good jQuery or JavaScript based solution available that refreshes the scroller div silently based on the situation I explained above in a given period of time?
You cannot have spaces in your variable name. function printDiv(any printable div) is a syntax error.
Try instead:
function printDiv(elId) {
var printContents = document.getElementById(elId).innerHTML;
var originalContents = document.body.innerHTML;
document.body.innerHTML = printContents;
window.print();
document.body.innerHTML = originalContents;
}
I assume from what you've stated that the intention here is to be able to print only a certain div, rather than an entire page. If that is the case then you are really going to extremes here to perform this function.
If you just want to make a prettier version of the page to print (one without menus, etc) look at How can I have different CSS when I print or print preview?. That question will show how to use a different set of css for printing, so you can set display:none; to anything that you don't want to display when printing.
If you want to print only a single div then you could look at the jQuery print element plugin. This is a simple js file that you link in your html then call
$([selector]).printElement();
to print just that element. Eg you could rewrite your method as so:
function printDiv(anyPrintableDiv) {
$('#' + anyPrintableDiv).printElement();
}
Good day.
I am currently working on a project that prints a desired <div> to a printer.
Here is the code:
var printContents = document.getElementById(id).innerHTML;
var originalContents = document.body.innerHTML;
document.body.innerHTML = printContents;
document.body.style.display = "none";
window.print();
document.body.innerHTML = originalContents;
document.body.style.display = "block";
This code works and prints the desired <div>, but after that I need to put back the previous page again so I used this statement:
document.body.innerHTML = originalContents;
document.body.style.display = "block";
This displays the previous page but the functionalities of my buttons are gone?! Can someone explain to me what happened and is there a solution to this problem? Thanks in advance!
This is happening because you've wiped out the old DOM which had events wired up to it, and replaced it with a totally new, different DOM that just happens to have the same HTML.
Presumably you're taking this approach because the printable zone is determined at runtime. A less-destructive solution might be to create a new <iframe> and copy the desired markup into that; then invoke print() on the iframe. Something like:
var printElement = function(element) {
var frame = document.createElement('iframe');
document.appendChild(frame);
frame.contentDocument.innerHTML = element.innerHTML;
frame.contentWindow.print();
document.removeChild(frame);
};
You'll also need to copy over any CSS references into the <iframe>.
(note this is pseudo-code and not tested)
Your code clears the document and then puts back the HTML stored in originalContents, but this variable stores only a string, so all previously registered event handlers are gone.
Why don't you create a print stylesheet and hide everything except the content that you want to print?
<link rel="stylesheet" type="text/css" href="print.css" media="print">
When you reset the innerHTML, you don't get all your event handlers back. They are wiped out when you create entirely new DOM elements.
One idea would be to have two master divs in the body, one that is your normal display and one that is what you want to print. You can then hide whichever one you don't want to display like this:
<body>
<div id="mainContent">main screen content goes here</div>
<div id="printContent">generated print content goes here</div>
</body>
// hide main content
var mainDiv = document.getElementById("mainContent");
mainDiv.style.display = "none";
// put content to print in the print div and show it
var printDiv = document.getElementById("printContent");
printDiv.innerHTML = document.getElementById(id).innerHTML;
printDiv.style.display = "block";
// print
window.print();
// restore visibility
mainDiv.style.display = "block";
printDiv.style.display = "none;
You could also just use the whole body for printing and use a stylesheet with media="print" to control the visibility of the things you do/don't want to print.
You can add a click event to all dives inside your page so that user can click the div.
after that add a class to that div which the class is defined within the print CSS file.
inside css print file use the following code:
`*{display:none}
.printableDiv{display:block}`
to define a print css file use this code :
<link rel="stylesheet" type="text/css" href="print.css" media="print"> (which Rafael has told you ).
good luck