Losing focus when using window.print() - javascript

While trying new things, which can make your life easier, I saw something that caught my eye. When you select some part of the page and then right-click on it, you can print this specific part. So.. my guess was "Use selection API and then print it!", but it didn't really go so well. I don't really know why and when, but I'm losing focus when trying to use window.print()
This is sample code which I was trying in console on stackoverflow. Any idea how to make it work? Maybe window.print() is wrong method to use here?
setTimeout(function () {
var el = document.getElementsByClassName('default prettyprint prettyprinted')[0];
window.getSelection().selectAllChildren(el);
window.print();
}, 3000);
What I'm selecting and then click printing
What I get printed

You are losing focus because the browser will start the native printing api, which usually also includes opening a print dialog or print preview. When interacting with that dialog with a click the text loses focus.(in Chrome, other browsers not tested)
With the use of monitorEvents I can show you "what happens"
monitorEvents(window);
window.getSelection().selectAllChildren(document.getElementsByClassName('post-text')[0]);
window.print();
pointerover PointerEvent {isTrusted: true, pointerId: 1, width: 1, height: 1, pressure: 0, …}
VM324:1 mouseover MouseEvent {isTrusted: true, screenX: -1644, screenY: 153, clientX: 276, clientY: 60, …}
VM324:1 pointermove PointerEvent {isTrusted: true, pointerId: 1, width: 1, height: 1, pressure: 0, …}
VM324:1 mousemove MouseEvent {isTrusted: true, screenX: -1644, screenY: 153, clientX: 276, clientY: 60, …}
VM324:1 pointerdown PointerEvent {isTrusted: true, pointerId: 1, width: 1, height: 1, pressure: 0.5, …}
VM324:1 mousedown MouseEvent {isTrusted: true, screenX: -1644, screenY: 153, clientX: 276, clientY: 60, …}
VM324:1 pointermove PointerEvent {isTrusted: true, pointerId: 1, width: 1, height: 1, pressure: 0.5, …}
VM324:1 mousemove MouseEvent {isTrusted: true, screenX: -1634, screenY: 205, clientX: 286, clientY: 112, …}
VM324:1 pointerup PointerEvent {isTrusted: true, pointerId: 1, width: 1, height: 1, pressure: 0, …}
VM324:1 mouseup MouseEvent {isTrusted: true, screenX: -1634, screenY: 205, clientX: 286, clientY: 112, …}
VM324:1 click MouseEvent {isTrusted: true, screenX: -1634, screenY: 205, clientX: 286, clientY: 112, …}
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Using chrome in this example I clicked a button in the printing dialog UI. This mouseup event caused the focus to go away and apparently the selection ending up cancelled. Possibly chrome inserts the printing dialog as a shadow DOM object, letting interactions on it having events for the document.
What I found out is that if I use the escape key to exit the dialog that this event is not transferred to the global event listeners, causing it to not having any effect and letting the selection persist.
So, if you wanted to print that selection you could use a code like the code below:
+function() {
// Create an iframe to make sure everything is clean and ordered.
var iframe = document.createElement('iframe');
// Give it enough dimension so you can visually check when modifying.
iframe.width = document.width;
iframe.height = document.height;
// Add it to the current document to be sure it has the internal objects set up.
document.body.append(iframe);
// Get the node you wish to print.
var origNode = document.querySelectorAll('.post-text .default.prettyprint.prettyprinted')[0];
// Clone it and all it's children
var node = origNode.cloneNode(true);
/**
* copied from https://stackoverflow.com/questions/19784064/set-javascript-computed-style-from-one-element-to-another
* #author Adi Darachi https://stackoverflow.com/users/2318881/adi-darachi
*/
var copyComputedStyle = function(from,to){
var computed_style_object = false;
//trying to figure out which style object we need to use depense on the browser support
//so we try until we have one
computed_style_object = from.currentStyle || document.defaultView.getComputedStyle(from,null);
//if the browser dose not support both methods we will return null
if(!computed_style_object) return null;
var stylePropertyValid = function(name,value){
//checking that the value is not a undefined
return typeof value !== 'undefined' &&
//checking that the value is not a object
typeof value !== 'object' &&
//checking that the value is not a function
typeof value !== 'function' &&
//checking that we dosent have empty string
value.length > 0 &&
//checking that the property is not int index ( happens on some browser
value != parseInt(value)
};
//we iterating the computed style object and compy the style props and the values
for(property in computed_style_object)
{
//checking if the property and value we get are valid sinse browser have different implementations
if(stylePropertyValid(property,computed_style_object[property]))
{
//applying the style property to the target element
to.style[property] = computed_style_object[property];
}
}
};
// Copy the base style.
copyComputedStyle(origNode, node);
// Copy over all relevant styles to preserve styling, work the way down the children tree.
var buildChild = function(masterList, childList) {
for(c=0; c<masterList.length; c++) {
var master = masterList[c];
var child = childList[c];
copyComputedStyle(master, child);
if(master.children && master.children.length > 0) {
buildChild(master.children, child.children);
}
}
}
if(origNode.children && origNode.children.length > 0) {
buildChild(origNode.children, node.children);
}
// Add the styled clone to the iframe. using contentWindow.document since it seems the be the most widely supported version.
iframe.contentWindow.document.body.append(node);
// Print the window
iframe.contentWindow.print();
// Give the browser a second to gather the data then remove the iframe.
window.setTimeout(function() {iframe.parentNode.removeChild(iframe)}, 1000);
}();

Related

iOS application fails to respond to mouse and touch events

I am developing an iOS application using Swift that incorporates web content by using WKWebView.
[URL Loading and Goal] Once a URL has been loaded inside this web view, I want to open a keyboard whenever the user wants to type.
[JavaScript Events] As browsing is based on eye coordinates, I want a JavaScript event to be triggered when the user's gaze is focused on an input field. On the basis of eye gaze coordinates, I can determine the underlying element of the webpage document. After this, I want to trigger a mouse event/touch event for clicks within the input element.
However, the system-level keyboard does not appear if I follow these steps.
Code:
touchPoint variable contains the current x and y coordinates of the eye gaze.
#IBOutlet var webView : WKWebView!
let jsStyle = """
function sendTouchEvent(x, y, element, eventType) {
const touchObj = new Touch({
identifier: Date.now(),
target: element,
clientX: x,
clientY: y,
radiusX: 2.5,
radiusY: 2.5,
rotationAngle: 10,
force: 0.5,
});
const touchEvent = new TouchEvent(eventType, {
cancelable: true,
bubbles: true,
touches: [touchObj],
targetTouches: [],
changedTouches: [touchObj],
shiftKey: true,
});
element.dispatchEvent(touchEvent);
}
function click(x, y)
{
var ev = new MouseEvent('click', {
'view': window,
'bubbles': true,
'cancelable': true,
'screenX': x,
'screenY': y
});
var el = document.elementFromPoint(x, y);
if(el.nodeName == "INPUT") {
el.autofocus = true;
el.click();
el.focus();
sendTouchEvent(x, y, el, 'touchstart');
}
try {
el.dispatchEvent(ev);
} catch(error) {
console.log(error);
}
}
click(\(touchPoint.x), \(touchPoint.y));
"""
webView.evaluateJavaScript(jsStyle, completionHandler : { result, error in
print("### Evaluate JavaScript for current click!")
} => Called when eye gaze coordinates are received.
[Temporary Solution] In order to make this work, I created a native UITextField that acts as a proxy for the webpage element and opens the keyboard by becoming the first responder when I determine using JS that the eye gaze is focused on an input element.
My goal, however, is to remove the proxy logic and replace it with JavaScript code that opens the system-level keyboard.

Prevent actual mouse movement from interfering with simulated mousemove event in javascript

I'm currently working on an automated tool that reproduce hand-drawing previously recorded on a canvas. I'm using fabric.js to create a canvas and free draw on it.
In this part of the application, the user can't draw anything but is watching the canvas being drawn on. That's why the canvas is behind an invisible div that blocks all interactions.
So far, I managed to recreate the drawing process using an asynchronous function and by triggering well-timed mouse events at specific locations.
Let's start with some mouse coordinates to use for the path to draw :
var coord = [
{x: 927, y: 115},
{x: 925, y: 116},
{x: 922, y: 116},
{x: 910, y: 115},
{x: 899, y: 114}
]
I use a custom function that simulates a mouse event at a specific location :
function simulateMouseEvent(e, eventName, x, y) {
e.dispatchEvent(new MouseEvent(eventName, {
view: window,
bubbles: true,
cancelable: true,
clientX: x,
clientY: y,
button: 0
}));
}
To start, I trigger a mousedown event as if the user was clicking on the canvas :
simulateMouseEvent(canvasElement, 'mousedown', coord[0].x, coord[0].y);
Then a for loop launches a bunch of consecutive mousemove events every 200 ms like so :
for (i = 1; i < coord.length; i++) {
await movement(coord[i]);
}
function movement(coordinates) {
return new Promise(function(resolve, reject) {
simulateMouseEvent(canvasElement, 'mousemove', coordinates.x, coordinates.y);
setTimeout(function() {
resolve('');
}, 200);
})
}
Finally, I stop the drawing by triggering a mouseup event like so :
var last = coord[coord.length - 1];
simulateMouseEvent(canvasElement, 'mouseup' last.x, last.y);
So everything works fine if the user's cursor is not above the canvas (which is what happens most of the time) even though there is a mask theoretically blocking the user's interaction with the canvas.
I believe that triggering a mouse event on a div "activates" it even if that div can't be reached. I tried adding pointer-events: none on the css rule of the canvas wih no difference. Is there a way to block real mouse cursor events while triggering fake ones ?

Attempting to simulate a wheel event [Firefox]

To clarify my example, I want to simulate a scroll event at the center of an open window. This should affect what is reasonably the main scrolling element on a given page.
Here are some relevant pages
https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/WheelEvent
https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent#Values
and here's what I've tried.
scroll_evt = new WheelEvent('wheel', {
deltaY: 10.0,
deltaMode: WheelEvent.DOM_DELTA_LINE,
clientX: content.innerWidth/2,
clientY: content.innerHeight/2
});
-> wheel { target: null, buttons: 0, clientX: 520, clientY: 320, layerX: 0, layerY: 0 }
However, despite no errors, dispatching the event seems not to have any effect.
window.dispatchEvent(scroll_evt);
-> true
So I'm left to wonder: Is there some critical property I'm missing in my initializer? Is window an appropriate target for the event? I'm out of ideas.
Update
This works.
https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIDOMWindowUtils#sendWheelEvent%28%29.
var windowutils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface (Components.interfaces.nsIDOMWindowUtils);
windowutils.sendWheelEvent(window.innerWidth / 2, window.innerHeight / 2, 0, 10, 0,
WheelEvent.DOM_DELTA_LINE, 0, 0, 0, 0);
I'm still curious what's the matter with dispatchEvent.
You can get an element by position like so:
var elem = document.elementFromPoint(content.innerWidth / 2, content.innerHeight / 2);
and then apply the scroll:
elem.scrollTop += 10;

How to adjust a jQuery function property value?

First of all, I'm pretty new to jQuery but with the help of http://css-tricks.com/examples/Circulate/ I managed to figure out how to create ellipses in my code.
Here's the thing though, I want to create an input field/text box for users to type in their own "speed" value (which is now set at "500") and then click a submit button to start the ellipse.
$("#ball").circulate({
speed: 500,
height: 500,
width: 200,
loop: true,
zIndexValues: [1, 1, 1, 1]
});
I figured out the submit button part, but I keep running in circles when it comes to finding a way for a visitor of my website to manually input the "speed". I would be much obliged if someone could point me in the right direction/could give me a starting point.
Try this:
$('#submitButton').click(function() {
var userSpeed = +$('#inputBox').val();
if (!userSpeed) {userSpeed == 500};
$("#ball").circulate({
speed: userSpeed,
height: 500,
width: 200,
loop: true,
zIndexValues: [1, 1, 1, 1]
});
});
Then, when the user clicks on the submit button, your code gets the new value and sets the speed for the function. If the user clicks the button without having entered a number, a default speed is used.
You have to use .val() function to get the value from an input and then use it to set the speed.
var input = $("#YOUR_INPUT_ID");
$("#ball").circulate({
speed: input.val(),
height: 500,
width: 200,
loop: true,
zIndexValues: [1, 1, 1, 1]
});

Titanium Window removes child views

I create a window A in Titanium (latest ver) and add some views which works well.
var winA = Titanium.UI.createWindow({
backgroundColor: bgColor,
statusBarHidden:true
});
var circle = createDragCircle('Dent'); //This is a Ti.Draggable view
winA.add(circle);
I then open a modal window B i.e:
winB.open({modal: true});
The winB opens fine but as it's, opening all the winA child views I've added are removed. I can tell it isn't reloading the winA.
Is this default behaviour?
Edit:
OK. On further investigation, it's not removing the added views. It resets them to the position they were at before the drag event. Essentially, I'm doing the following:
var drag = require('ti.draggable');
var win = Titanium.UI.createWindow({
backgroundColor: bgColor,
statusBarHidden:true
});
var circle = drag.createView({
height: 30,
width: 30,
zIndex: 100,
top: top,
left: 25,
minLeft: 65,
maxLeft: 285,
minTop: 105,
maxTop: 370,
isDragged: false,
type: type
});
//Add an event listener to catch drag end
circle.addEventListener('end', function(e) {
var editwin; //Call to create winB
editwin.open({modal: true});
});
winB opens fine but the circle object moves back to the place it was before it was dragged.
if you are opening winB within winA with modal property then it will create a problem.
Thanks for help all.
I sorted it out by doing the following:
circle.addEventListener('end', function(e) {
this.top = e.source.top;
this.left = e.source.left;
});
This forces the object to take on the drop top & left properties.

Categories

Resources