Phantomjs dump dom object - phantomjs

I'm new to Phantomjs. For debugging on a remote server, I often want to dump a DOM object to look at the structure (similar to Data::Dumper in Perl). This currently is for scraping a couple of sites.
I've thought JSON.stringify may help with this, but it still displays an object name like "[object HTMLDocument]"
Edit: I have also looked at JavaScript: how to serialize a DOM element as a string to be used later? , but I can't seem to inject jquery in phantomjs (still looking for a solution to that, and would prefer no depencencies), and the other answer doesn't seem to work. As I assume it would be a common case for Phantom to analyse the DOM, I thought it would be common for phantom users to have a solution to this.
var page = require('webpage').create();
var system = require('system');
page.onConsoleMessage = function(msg) {
console.log( msg );
}
page.open('http://www.test.com', function(status) {
if(status !== "success") {
console.log( status );
} else {
page.evaluate(function() {
var headline = document.querySelectorAll('div');
console.log( JSON.stringify( headline ) ); // HERE???
});
}
phantom.exit();
});
Is there any way to do this, or am I approaching this wrong ?

in page.evaluate(), you can use XMLSerializer.serializeToString() to convert whatever DOM node you want to string.
page.evaluate(function() {
var s = new XMLSerializer();
return s.serializeToString(document.getElementById('div'));
});
I haven't tried it with "querySelectorAll", since it may return array instead of standalone DOM node, but it definitely works for DOM nodes.
MDN Link

Related

SyncXHR in Page Dismissal Alternative

Since google has declared to disallow sync XHR in page dismissal, i havent found the decent replacement to this feature. I've tried sendBeacon, but the 64KB payload limit makes it useless for my use case. At this point, i found the workaround by configuring the chromium flag directly (#allow-sync-xhr-in-page-dismissal). But this is clearly not the final solution. It's not user friendly to force your user to tweak their own browser in order to use our app.
Is there any syncXHR in page dismissal alternative?
var xhr;
function saveChanges(){
xhr = new XMLHttpRequest();
xhr.open('POST',url,false)
xhr.send(post)
}
window.addEventListener('beforeunload', (event) =>{
saveChanges();
if(xhr.readyState == 4) return;
event.preventDefault();
event.returnValue = '';
})
Credit to : https://groups.google.com/a/chromium.org/g/blink-dev/c/LnqwTCiT9Gs/m/wM0yAjcfAAAJ

formData get() Doesn't seem to work in Safari

This is my code. It works in Firefox and Chrome but not Safari. I get no errors.
<script>
var cleanData = new FormData();
cleanData.append("test", "test");
alert(cleanData.get("test"));
</script>
Does anyone know a workaround?
Apparently, Safari has no means of getting values stored in FormData objects at this time. There is no workaround at this time, and apparently it's not practical to polyfill.
Sorry :(
Notes:
https://developer.mozilla.org/en-US/docs/Web/API/FormData/get#Browser_compatibility
https://www.bountysource.com/issues/27573236-is-it-possible-to-polyfill-missing-formdata-methods
I solved this by conditionally (if Safari is the browser) iterating through the elements property of an actual form. For all other browser, my wrapper just iterates through FormData entries(). The end result of my function, in either case, is a simple javascript object (JSON) which amounts to name/value pairs.
function FormDataNameValuePairs(FormName)
{
var FormDaytaObject={};
var FormElement=$('#'+FormName).get(0);
if (IsSafariBrowser())
{
var FormElementCollection=FormElement.elements;
//console.log('namedItem='+FormElementCollection.namedItem('KEY'));
var JQEle,EleType;
for (ele=0; (ele < FormElementCollection.length); ele++)
{
JQEle=$(FormElementCollection.item(ele));
EleType=JQEle.attr('type');
// https://github.com/jimmywarting/FormData/blob/master/FormData.js
if ((! JQEle.attr('name')) ||
(((EleType == 'checkbox') || (EleType == 'radio')) &&
(! JQEle.prop('checked'))))
continue;
FormDaytaObject[JQEle.attr('name')]=JQEle.val();
}
}
else
{
var FormDayta=new FormData(FormElement);
for (var fld of FormDayta.entries())
FormDaytaObject[fld[0]]=fld[1];
}
return FormDaytaObject;
}
where IsSafariBrowser() is implemented by whatever your favorite method is, but I chose this:
function IsSafariBrowser()
{
var VendorName=window.navigator.vendor;
return ((VendorName.indexOf('Apple') > -1) &&
(window.navigator.userAgent.indexOf('Safari') > -1));
}
Example usage in OP's case, assuming that you have an actual form called CleanDataForm instead of creating a FormData from scratch:
var cleanData=FormDataNameValuePairs('CleanDataForm');
alert(cleanData.test);

getting full working single page with phantomjs

I'm trying to get this page: http://www.pqllana.com.ar/distribuidores/mapa with phantomjs.
I have special interest in getting the section that contains "ubicacion", "locales", "mapa".
As you can see in the page, it works with javascript, and I want to get those sections fully working, I mean that if I click on them they should work as expected.
What happens is that the google map is not loaded correctly, and some links doesn't work at all. I'm retrieving the page using this code:
var page = require('webpage').create();
page.open('http://www.pqllana.com.ar/distribuidores/mapa', function() {
var content = page.content;
var fs = require('fs');
try {
fs.write("hellohello.ctp", content, 'w');
} catch(e) {
console.log(e);
}
phantom.exit();
});
What I do is I pick that file and render into another page.
Looks like what I'm trying to achieve is not possible with PhantomJS (not suitable for this task), so I'm going to implement an iFrame, deactivate it's scrollbar and use dynamic size.

How to use callbacks in phantomjs

I am using phantomjs for testing one web application. but i am facing problem with page load means sometimes phantom script executed but dom element is not loaded. How to use callbacks for sorting this kind of issues
resourceReceived(request),resourceRequested(resource),resourceError(resource)
If you want to execute code after the page has finished loading, use this:
page.onLoadFinished = function()
{
// function body
var pageTitle = page.evaluate(function() {
console.log('Page Name: ' + document.title);
return document.title;
});
};

Detecting browser print event

Is it possible to detect when a user is printing something from their browser?
To complicate matters, if we are presenting a user with a PDF document in a new window is it possible to detect the printing of that document ( assuming the user prints it from the browser window)?
The closest I've been able to find is if we implement custom print functionality (something like this) and track when that is invoked
I'm primarily interested in a solution that works for internet explorer (6 or later)
You can now detect a print request in IE 5+, Firefox 6+, Chrome 9+, and Safari 5+ using the following technique:
(function() {
var beforePrint = function() {
console.log('Functionality to run before printing.');
};
var afterPrint = function() {
console.log('Functionality to run after printing');
};
if (window.matchMedia) {
var mediaQueryList = window.matchMedia('print');
mediaQueryList.addListener(function(mql) {
if (mql.matches) {
beforePrint();
} else {
afterPrint();
}
});
}
window.onbeforeprint = beforePrint;
window.onafterprint = afterPrint;
}());
I go into more detail into what this is doing and what it can be used for at http://tjvantoll.com/2012/06/15/detecting-print-requests-with-javascript/.
For Internet Exploder, there are the events window.onbeforeprint and window.onafterprint but they don't work with any other browser and as a result they are usually useless.
They seem to work exactly the same for some reason, both executing their event handlers before the printing window opens.
But in case you want it anyway despite these caveats, here's an example:
window.onbeforeprint = function() {
alert("Printing shall commence!");
}
For anyone reading this on 2020.
The addListener function is mostly deprecated in favor of addEventListener except for Safari:
if (window.matchMedia) {
const media = window.matchMedia("print");
const myFunc = mediaQueryList => {
if (mediaQueryList.matches) {
doStuff();
}
};
try {
media.addEventListener("change", myFunc);
} catch (error) {
try {
media.addListener(myFunc);
} catch (error) {
console.debug('Error', error)
}
}
}
Reference: This other S.O question
If it's only for tracking purposes, perhaps you could set a background url in CSS print media to a server page (.aspx, .php, etc) and then do something on the server?
This guy claims it works.
This is not as versitile as TJ's solution, but it may be less buggy (see TJs blog post for issues he found) when only tracking is needed.