Print iFrame only when CSS is loaded completely - javascript

I’m printing selected parts of pages by putting them in an empty iframe. Everthing is fine except the CSS. Sometimes it’s there, sometimes not, so I guess the print function is loaded in moste cases before the css is complete. Unforntunately I’m obviously not able to solver the problem. The basic code:
var pStyles = new String ("<link href='/css/print.css' rel='stylesheet' type='text/css' id='cssprint' />");
var pWrapIn = new String ("<main><article><section>");
var pWrapOut = new String ("</section></article></main>");
function printFrame(fId) {
window.frames["printhelper"].document.body.innerHTML = pStyles + pWrapIn + document.getElementById(fId).innerHTML + pWrapOut;
console.log(window.frames["printhelper"].document.body.innerHTML);
window.frames["printhelper"].window.focus();
window.frames["printhelper"].window.print();
}
This works, but with the described CSS issue. To make sure the CSS is loaded, I ended up with the modified function, which is not working at all:
function printFrame(fId) {
window.frames["printhelper"].document.body.innerHTML = pStyles + pWrapIn + document.getElementById(fId).innerHTML + pWrapOut;
console.log(window.frames["printhelper"].document.body.innerHTML);
$(#cssprint).on('load', function() {
console.log ('CSS loaded');
window.frames["printhelper"].window.focus();
window.frames["printhelper"].window.print();
}, 0);
}
#cssprint seems to be loaded never, so console.log stays empty and no print is done. But what am I doing wrong? (The function printFrame is called by a simple onClick in the HTML markup.)

I would probably do something like this:
Add onload event handler to CSS link that calls a function that posts a message to the parent window (parent.postMessage(...)) or directly does so, then you do not need an extra script tag.
The outer window listens to the message of the iframe window and starts printing when receiving it.
You can also just skip the message posting if you do not need to synchronize anything with the parent window an just print directly in the onload event:
<link onload="window.print()" ...>
See MDN for more info on posting and receiving messages.

The complete working function (thanks to H. B.):
var pStyles = new String ("<link href='/css/print.css' rel='stylesheet' type='text/css' onload='window.focus(); window.print()' />");
var pWrapIn = new String ("<main><article><section>");
var pWrapOut = new String ("</section></article></main>");
function printFrame(fId) {
window.frames["printhelper"].document.body.innerHTML = pStyles + pWrapIn + document.getElementById(fId).innerHTML + pWrapOut;
}

Related

Passing json object in href function - JavaScript

I need to pass a json object to function, which is as mentioned below in a href, but this JS code is not getting evaluated. So can anyone suggest a workaroud or a solution for this?
function function_test(option,jsonObj){
displayMessage(str);
}
function function_prepare_div(){
var str ="";
var jsonResposneObj = getJson();//function to get jsonResponseObj
for(i=0;i<jsonResponseObj.length;i++){
str += "<a href='function_test( " + i + "," + jsonResposneObj.dataObj[i] +")'>1. " + jsonResposneObj.dataObj[i].objName + "</a></br>";
}
return str;
}
P.S. I cannot return the jsonResponse after function call.
Instead of using inlined JS, append the element using proper DOM methods, and then attach an event listener.
Eg something like
const a = container.appendChild(document.createElement('a'));
a.href = function_test(1, jsonResponse);
a.textContent = '1. ' + function_test(1, jsonResponse);
(make sure function_test returns a URL)
Functions (or any JavaScript for that matter) don't belong in an <a href=""> in the first place.
Hyperlinks are for navigation, not JavaScript hooks. Using them just to trigger some JavaScript is an incorrect use of the <a> tag. It was commonplace 20+ years ago (before we had web standards), but is woefully outdated and downright incorrect today. Using a hyperlink to trigger JavaScript is semantically incorrect and can cause issues for people who rely on assistive technologies (like screen readers) to navigate a page.
Just about any element that is valid in the body of a web page supports a click event and most are better suited to what you want to do.
What you need to do is register your function as the callback to the click event of some element, like this:
// An example of a JSON response
var jsonResponse = '{"key1":10,"key2":true,"key3":"foo"}';
// Get reference to any element that supports a click event that
// can be safely used as a JavaScript trigger
var span = document.getElementById("fakeLink");
// Set up an event handling callback function for the click event of the element
span.addEventListener("click", function(){
// Call the function and pass it any arguments needed
processJSON(1, jsonResponse, this);
});
// Do whatever you need to do in this function:
function processJSON(val, json, el){
console.log(val, json);
el.textContent = val + json;
}
/* Make element look & feel like a hyperlink */
#fakeLink { cursor:pointer; text-decoration:underline; }
#fakeLink:active { color:red; }
<span id="fakeLink">Click Me</span>
var json = {
foo: 'bar'
};
var func = function(data) {
alert(data.foo);
}
$('body').append("<button onclick='func(" + JSON.stringify(json) + ")'>BUTTON</button>");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Javascript Print() sometimes firing print layout/paper size options and sometimes doesn't

This is the exact same function I am using in two different parts of my application. (Which is an Ember application). However, in the first instance, when you fire this function, the application opens a new window with the html data on it, then opens a print dialog with the options layout and paper size.
In the second instance, these options are not included. Why would this happen? Or, if there is a better way to format for printing, what am I doing wrong?
printPreview: function() { //For Printing Issue Document
var data = $('#editDoc').editable('getHTML', false, true);
data = '<div class="froala-view">' + data + '</div>';
data = '<link rel="stylesheet" href="styles/froala_editor.css"> <link rel="stylesheet" href="styles/froala_content.css"><link rel="stylesheet" href="styles/froala_style.css">' + data;
// debugger
var printWindow = window.open('', '_blank', 'width=825,height=590');
printWindow.document.write(data);
printWindow.document.close();
setTimeout(function() { //content loaded after 1 sec print will invoke
printWindow.print();
}, 1000);
},

Call to window.print firing before documents style is applied

I have a form with a print function. Inside the function I open a window, build the document, add the head with style sheets, add the form HTML and make the call to print the last part of the body. The problem is the initial print preview/print doesn't reflect the style. If I cancel the print and attempt to print manually, the style shows up.
I've tried quite a few ways of doing this with no luck. It seems like a timing issue. Any ideas?
Browser is Chrome. Below is the JS function. (This is injected with a faces context).
function printForm(windowTitle, path){
var printWindow = window.open();
var printDocument = printWindow.document;
var headHtml = "<link href='" + path + "/css/style.css' rel='stylesheet' type='text/css'/>";
printDocument.getElementsByTagName('head')[0].innerHTML = headHtml;
var printDiv = printDocument.createElement("DIV");
var formDiv = document.getElementById("formDiv");
printDiv.innerHTML = formDiv.innerHTML // Styling here is the issue
printDocument.body.appendChild(printDiv);
var scriptTag = printDocument.createElement("script");
var script = printDocument.createTextNode("print();");
scriptTag.appendChild(script);
printDocument.body.appendChild(scriptTag);
}
I think you need a setTimeout to delay printing for a second or two so the style can be applied. You'll need to tinker to find the right length.
Instead of trying to guessing with a timer, it might be more precise to use the load event on the <link ...> to know exactly when it has completed loading.
Below is a function I use which illustrates this technique. It uses an off-screen <iframe> to print a portion of a page. It's jQuery based, but the principles are the same in vanilla JS. Note the setting of the <link>'s onload function with an anonymous function which invokes print() on the <iframe>'s window object.
/*
* expects:
* <tag id="whatever">...goop...</tag>
* <input type="button" class="printit" data-target="whatever" value="Print">
*/
$(function() {
$( '.printit' ).click(function() {
var goop = $( '#' + this.dataset.target ).prop('outerHTML');
var ifr = $('<iframe>');
var bas = $('<base href="' + document.baseURI + '">');
var lnk = $('<link rel="stylesheet" href="/css/print.css">');
lnk.on('load', function() { /* CSS is ready! */
ifr[0].contentWindow.print();
});
ifr.css( { position: 'absolute', top: '-10000px' } )
.appendTo('body')
.contents().find('body').append(goop);
ifr.contents().find('head').append(bas).append(lnk);
return false;
});
});

Loading an image but onload/onerror not working as expected

I have a div
<div id='cards'>
Which I want to fill with images based on some logic. But only when images are first loaded into memory. Otherwise, through onerror I wanna fill in some text..
function pasteCard(card, to){
if (typeof(card) == 'string')
card = [card];
var image = [];
for (var i = 0; i < card.length; i++) {
image[i] = new Image();
image[i].src = '/sprites/open/' + card[i] + '.png';
image[i].onload = function() {
pasteImage(to, image[i]);
}
image[i].onerror = function() {
pasteText(to, card[i]);
}
// alert(card[i]) #1
}
function pasteImage(to, image) {
to.append(image);
}
function pasteText(to, text) {
// alert(card[i]) #2
to.append(text);
}
}
pasteCard(['ABC123', 'DEF456', 'GHI789'], $('#cards'));
But this isn't working.
Problem/weirdness: If only #2 alert is active it returns nothing. But strangely if #1 alert is also active it does kinda work... (but still doesn't load my images, and mostly fails too when other code is involved)
Question: Why is it not working without #1 alert (at least in that jsfiddle)
suggestions?: what should I do?
Onload and onerror events are fired (executed) outside the scope of your function so your variables will be undefined. In the event method you have access to this which is the image object. You can set a data attribute to each image and access that in your error event.
Here is an example:
http://jsfiddle.net/7CfEu/4/
The callbacks are not in the same scope as your image array is - therefor you need to declare a variable then will "connect the scopes" and use it inside the callbacks
also the i variable probably changes until the callback is fired - so by using it inside the callback you will get undefined behavior
for (var i = 0; i < card.length; i++) {
var current_card = card[i];
var current_image = new Image();
current_image.onload = function() {
pasteImage(to, current_image);
}
current_image.onerror = function() {
pasteText(to, current_card);
}
current_image.src = '/sprites/open/' + current_card + '.png';
image[i] = current_image;
}
Fiddle: http://jsfiddle.net/7CfEu/6/
(Also - closing the div tag is never a bad idea)
Just in case anyone ends up here for same reason I did.
Was going crazy because onload and onerror were not firing in the page I was building. Tried copy pasting
var myimage = new Image();
myimage.onload = function() { alert("Success"); };
myimage.onerror = function() { alert("Fail"); };
myimage.src = "mog.gif" //Doesn't exist.
Which was working within codepen and random otherwise blank pages.
Turns out the problem I was having was that I was doing AJAX requests earlier in the page. This involved authorization which in turn involved a call to
setRequestHeader();
This was resulting in a net::ERR_FILE_NOT_FOUND error instead of the expected GET mog.gif 404 (Not Found)
This seemed to prevent proper triggering of events.
Revert with
xhr.setRequestHeader("Authorization", "");

Check popup window status

When someone requests a chat, an entry is made in the database. I have an hidden iframe on our dashboard that checks the database every 20 seconds to see if there is a chat and if there is it launches a popup window. Even if the popup is open the iframe still refreshes the popup every 20 seconds. Want I am trying to achieve is a javascript to check the status of the popup. If it is closed I want it to reopen it... if it is open then it bring it into focus... but I dont want the popup to refresh.. as I have an ajax script doing this..
Here is my code:
<script language="javascript" type="text/javascript">
function myOpenWindow(winURL, winName, winFeatures, winObj)
{
var theWin;
if (winObj != null)
{
if (!winObj.closed)
{
winObj.focus();
return winObj;
}
}
else
{
theWin = window.open(winURL, winName, winFeatures);
return theWin;
}
}
</script>
<% IF ChatSessionID <> "" THEN %>
<script language="javascript" type="text/javascript">
var gmyWin = null;
window.onload = function()
{
var w = 900;
var h = 500;
var l = (screen.width-w)/2;
var t = (screen.height-h)/2;
var params = 'status=0,resizable=0,scrollbars=0,width=' + w + ',height=' + h + ',left=' + l + ',top=' + t;
gmyWin = myOpenWindow("/chat/chat_window.asp?ChatSession=<%=ChatSessionID%>&id=3", "myWin", params, gmyWin)
}
</script>
<% END IF %>
Any help you could provide would be greatly appreciated..
Best Regards,
Paul
I am not sure, but I believe if you name the window (e.g. myWin) when you call window.open, then later call window.open again using the same name, it will return the existing window if its already open or open/re-open the window and return a handle to that.
Edit
Ah, there you go -- from window.open:
If a window with the name
strWindowName already exists, then,
instead of opening a new window,
strUrl is loaded into the existing
window. In this case the return value
of the method is the existing window
and strWindowFeatures is ignored.
Providing an empty string for strUrl
is a way to get a reference to an open
window by its name without changing
the window's location. If you want to
open a new window on every call of
window.open(), you should use the
special value _blank for
strWindowName.
I believe according to the above mentioned specs, this might work:
var hChatWindow = window.open("", "ChatWindow", "whatever features"); // url intentionally left blank
// hChatWindow now contains a reference to new, existing or re-opened window
hChatWindow.focus();
if (hChatWindow.location=="about:blank") { // not sure; you need to experiment here
hChatWindow.location = "/chat/chat_window.asp?whatever";
}
Demo here, source here.
Register a callback on gmyWin.onunload.
You will find it tricky to subvert "block pop-up windows" in most browsers. However, if it is disabled, the following will work.
Main window:
var status = false;
function winOpen(){
window.open("child.html");
}
function winStatus(){
alert(status);
}
Pop-up window:
window.opener.status = true;
window.onblur = window.focus;
window.onunload = function(){
window.opener.status = false;
};

Categories

Resources