JSON.stringify losing properties - Logging and saving all javascript events - javascript

I am trying to log and save all the events that happens in a user session.
In order to do that, I'm having an array
var events = []
Having a listener for every event..
window.onload = function() {
window.addEventListener("abort", handleEvent);
window.addEventListener("blur", handleEvent);
[...]
}
and sending the events:
function sendEvents() {
axios.post('/api/logger', JSON.stringify(events));
}
The problem is that every sendEvents() is sending an array of events, but each of them have only two properties:
[{"isTrusted":true,"date":"2019-02-19T14:59:42.474Z"},{"isTrusted":true,"date":"2019-02-19T14:59:42.485Z"}
But it should have a LOT more properties, for example:
*altKey: false
bubbles: true
button: 0
buttons: 0
cancelBubble: false
cancelable: true
clientX: 662
clientY: 273
composed: true
ctrlKey: false
currentTarget: null
date: Tue Feb 19 2019 12:08:38 GMT-0300 (hora estándar de Argentina) {}
defaultPrevented: false
detail: 0
eventPhase: 0
fromElement: null
isTrusted: true
layerX: 662
layerY: 273
metaKey: false
movementX: 0
movementY: 0
offsetX: 662
offsetY: 273
pageX: 662
pageY: 273
path: (7) [header.App-header, div.App, div#root, body, html, document, Window]
relatedTarget: null
returnValue: true
screenX: 662
screenY: 400
shiftKey: false
sourceCapabilities: InputDeviceCapabilities {firesTouchEvents: false}
srcElement: header.App-header
target: header.App-header
timeStamp: 1605.000000000473
toElement: header.App-header
type: "mouseover"
which: 1
x: 662
y: 273*
Even if I don't stringify the array, in the backend the object have only the same two attributes. (I'm using nodejs + mongodb)
How can I save the entire event object with all its properties?

There could be a couple reasons for this. Most likely is those properties are not enumerable which causes them to not be included in JSON. (Properties are defined as enumerable via Object.defineProperties.) See:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description
I would recommend explicitly declaring which properties you want to POST, since you probably don't need all of them anyway:
axios.post('/api/logger', JSON.stringify(events.map(event=>{
return {
altKey: event.altKey,
bubbles: event.bubbles,
// etc...
}
}));

Related

How to fire Blur event to window with javascript

I set a text in a field with javascript, then I send change event to that field.
I would like to fire blur event on the window as the website require.
to set the text in the input field I use "document.getElementsByTagName('input')[23].value = 'London'"
to send change event to the input field I use
var o = document.getElementsByTagName('input')[23];
if (document.createEvent) {
var evt = document.createEvent('Events');
evt.initEvent('change', true, true);
o.dispatchEvent(evt); }
now I would like to send blur event but to window, I don t know how to do that.
this is the source code for the field :
<span class="next-input next-error next-large address-input">
<input height="100%" autocomplete="off" value="" data-spm-anchor-id="a2g0s.8850659.0.i9.2bdb6c37UwO8uq"></span>
this is the process that the website do :
change Event {isTrusted: true, type: "change", target: input, currentTarget: Window, eventPhase: 3, …}
blur
Event {isTrusted: true, type: "blur", target: Window, currentTarget: Window, eventPhase: 2, …}
bubbles: false
cancelBubble: false
cancelable: false
composed: false
currentTarget: Window {parent: Window, opener: null, top: Window, length: 1, frames: Window, …}
defaultPrevented: false
eventPhase: 0
isTrusted: true
path: [Window]
returnValue: true
srcElement: Window {parent: Window, opener: null, top: Window, length: 1, frames: Window, …}
target: Window {parent: Window, opener: null, top: Window, length: 1, frames: Window, …}
timeStamp: 30631177.38000001
type: "blur"
__proto__: Event

Getting specific properties of custom object

I have a CustomEvent object that I need to retrieve specific properties for. This object is not a plain object, but rather is the result of new CustomEvent();. When I output this object in the [Chrome] browser console, I see the following:
CustomEvent{
bubbles: false
cancelBubble: false
cancelable: false
composed: false
currentTarget: Window {...}
defaultPrevented: false
detail: {...}
eventPhase: 0
isTrusted: false
path: [Window]
returnValue: true
srcElement: Window {...}
target: Window {...}
timeStamp: 4979.4150000670925
type: "resize_element"
}
When I use Object.keys(custom_event_obj), I get only one key, isTrusted. This would mean that isTrusted is the only enumerable key.
I use the following code to make sure:
for(var key_str in custom_event_obj)
{
console.log(key_str, 'property is enumerable =', custom_event_obj.propertyIsEnumerable(key_str))
}
And I get the following result:
isTrusted is enumerable = true
detail is enumerable = false
initCustomEvent is enumerable = false
NONE is enumerable = false
CAPTURING_PHASE is enumerable = false
AT_TARGET is enumerable = false
BUBBLING_PHASE is enumerable = false
type is enumerable = false
target is enumerable = false
currentTarget is enumerable = false
eventPhase is enumerable = false
bubbles is enumerable = false
cancelable is enumerable = false
defaultPrevented is enumerable = false
composed is enumerable = false
timeStamp is enumerable = false
srcElement is enumerable = false
returnValue is enumerable = false
cancelBubble is enumerable = false
path is enumerable = false
composedPath is enumerable = false
stopPropagation is enumerable = false
stopImmediatePropagation is enumerable = false
preventDefault is enumerable = false
initEvent is enumerable = false
Object.getOwnPropertyNames gives me only one key as well: isTrusted.
This list contains keys that don't appear in the browser console result.
I'd like to get just the keys that appear in the browser console. How do I filter those keys out from the full list above?
Object.getOwnPropertyNames gives me only one key as well: isTrusted.
Not for me:
let CustomEvent = {
bubbles: false,
cancelBubble: false,
cancelable: false,
composed: false,
currentTarget: null,
defaultPrevented: false,
detail: null,
eventPhase: 0,
isTrusted: false,
path: [],
returnValue: true,
srcElement: null,
target: null,
timeStamp: 4979.4150000670925,
type: "resize_element"
}
console.log(Object.getOwnPropertyNames(CustomEvent));
But, while that does get you the property names, you'll still have to loop over the object and get the values for the properties.
You can also use .hasOwnProperty to only enumerate the properties that are not inherited.
let CustomEvent = {
bubbles: false,
cancelBubble: false,
cancelable: false,
composed: false,
currentTarget: null,
defaultPrevented: false,
detail: null,
eventPhase: 0,
isTrusted: false,
path: [],
returnValue: true,
srcElement: null,
target: null,
timeStamp: 4979.4150000670925,
type: "resize_element"
}
for(var prop in CustomEvent){
if(CustomEvent.hasOwnProperty(prop)){
console.log(prop, CustomEvent[prop]);
}
}

How to get the textContent length of an event

I would like to retrieve the number of elements in an event.
I use the following function :
element.bind('keyup', function (event) {
console.log(event);
});
The console.log (event) resitute me an object like this one (object shortened for the rendering)
n.Event {originalEvent: KeyboardEvent, type: "keyup", isDefaultPrevented:
ƒ, timeStamp: 13561.044999980368, jQuery2140638708200199875: true, …}
altKey: false
bubbles: true
cancelable: true
char: undefined
charCode: 0
...
scrollTop: 0
scrollWidth: 1035
shadowRoot: null
slot: ""
spellcheck: true
style: CSSStyleDeclaration {alignContent: "", alignItems: "", alignSelf: "",
alignmentBaseline: "", all: "", …}
tabIndex: 0
tagName: "DIV"
textContent: "abc"
title: ""
translate: true
...
In this object I would like to retrieve in variable the value of the length of the textContent
textContent: "abc"
Here is a example :
https://codepen.io/gregand/pen/PoYevYQ?editors=1111
How can I recover this value ?
To achieve expected result, use below code
console.log(event.target.childNodes[0].length)
working code for reference - https://codepen.io/nagasai/pen/JjPvJoL?editors=1111

Inaccessible array returned by Firefox WebExtensions browser.tabs.query(). Can someone explain why?

Can someone explain to me what is going on here? I am playing around with the Firefox WebExtensions API, and I am running into some rather strange behavior. Here is some very simple to code to illustrate the issue.
I am querying for all pinned tabs in all windows and I am expecting that the global array var pinned = []; will be populated with the tab data/objects. However, things are a little screwy to say the least. The array seems to be populated but the individual elements are not accessible using standard array notation pinned[0] returns undefined. I am absolutely perplexed by this.
So what is going on here? Is this an issue about scope or permissions?
Now the code ...
[manifest.json]
{
"manifest_version": 2,
"name": "test",
"version": "1.0",
"description": "test",
"icons": {
"48": "icons/page-48_white.png"
},
"permissions": [
"tabs",
"<all_urls>"
],
"background": {
"scripts": ["background.js"]
},
"devtools_page": "test.html",
"browser_action": {
"default_icon": { "48": "icons/page-48_white.png"},
"default_title": "Test",
"browser_style": true
}
}
[background.js]
function createExtPage(){
browser.tabs.create({ "url": "test.html" });
}
browser.browserAction.onClicked.addListener(createExtPage);
[test.js]
var pinned = [];
browser.tabs.query({pinned: true})
.then(tabs => {
for (let [key, value] of tabs.entries() ) {
console.log("Object " + key, value);
pinned.push(value);
}
});
console.debug("All Pinned Tabs", pinned);
console.debug("First Pinned Tab", pinned[0]);
Now take a look at this output from the Firefox developer tools console.
All Pinned Tabs
[]
0: {…}
active: false
audible: false
discarded: false
favIconUrl: "http://www.crunchyroll.com/favicon.ico?v=1"
height: 800
hidden: false
highlighted: false
id: 149
incognito: false
index: 0
isArticle: false
isInReaderMode: false
lastAccessed: 1522229700160
mutedInfo: Object { muted: false }
pinned: true
sharingState: Object { camera: false, microphone: false }
status: "complete"
title: "Crunchyroll - Watch Naruto Shippuden, Bleach, Anime Videos and Episodes Free Online"
url: "http://www.crunchyroll.com/"
width: 1440
windowId: 3
__proto__: Object { … }
​
1: {…}
active: false
audible: false
discarded: false
favIconUrl: "https://www.pandora.com/favicon.ico"
height: 800
hidden: false
highlighted: false
id: 145
incognito: false
index: 0
isArticle: false
isInReaderMode: false
lastAccessed: 1522447564848
mutedInfo: Object { muted: false }
pinned: true
sharingState: Object { camera: false, microphone: false }
status: "complete"
title: "All Along The Watchtower Radio - Now Playing on Pandora"
url: "https://www.pandora.com/station/play/3395036678172411653"
width: 1440
windowId: 67
__proto__: Object { … }
​
2: {…}
active: false
audible: false
discarded: false
height: 800
hidden: false
highlighted: false
id: 171
incognito: false
index: 1
isArticle: false
isInReaderMode: false
lastAccessed: 1522398347238
mutedInfo: Object { muted: false }
pinned: true
sharingState: Object { camera: false, microphone: false }
status: "complete"
title: "Debugging with Firefox Developer Tools"
url: "about:debugging"
width: 1440
windowId: 67
__proto__: Object { … }
length: 3
​ __proto__: Array []
First Pinned Tab undefined
Object 0
{…}
active: false
audible: false
discarded: false
favIconUrl: "http://www.crunchyroll.com/favicon.ico?v=1"
height: 800
hidden: false
highlighted: false
id: 149
incognito: false
index: 0
isArticle: false
isInReaderMode: false
lastAccessed: 1522229700160
mutedInfo: Object { muted: false }
pinned: true
sharingState: Object { camera: false, microphone: false }
status: "complete"
title: "Crunchyroll - Watch Naruto Shippuden, Bleach, Anime Videos and Episodes Free Online"
url: "http://www.crunchyroll.com/"
width: 1440
windowId: 3
__proto__: Object { … }
Object 1
{…}
active: false
audible: false
discarded: false
favIconUrl: "https://www.pandora.com/favicon.ico"
height: 800
hidden: false
highlighted: false
id: 145
incognito: false
index: 0
isArticle: false
isInReaderMode: false
lastAccessed: 1522447564848
mutedInfo: Object { muted: false }
pinned: true
sharingState: Object { camera: false, microphone: false }
status: "complete"
title: "All Along The Watchtower Radio - Now Playing on Pandora"
url: "https://www.pandora.com/station/play/3395036678172411653"
width: 1440
windowId: 67
__proto__: Object { … }
Object 2
{…}
active: false
audible: false
discarded: false
height: 800
hidden: false
highlighted: false
id: 171
incognito: false
index: 1
isArticle: false
isInReaderMode: false
lastAccessed: 1522398347238
mutedInfo: Object { muted: false }
pinned: true
sharingState: Object { camera: false, microphone: false }
status: "complete"
title: "Debugging with Firefox Developer Tools"
url: "about:debugging"
width: 1440
windowId: 67
__proto__: Object { … }
Here is the really screwy thing. If I type pinned[0] in the firefox developer tools console I get this result
pinned[0]
{…}
active: false
audible: false
discarded: false
favIconUrl: "http://www.crunchyroll.com/favicon.ico?v=1"
height: 800
hidden: false
highlighted: false
id: 149
incognito: false
index: 0
isArticle: false
isInReaderMode: false
lastAccessed: 1522229700160
mutedInfo: Object { muted: false }
pinned: true
sharingState: Object { camera: false, microphone: false }
status: "complete"
title: "Crunchyroll - Watch Naruto Shippuden, Bleach, Anime Videos and Episodes Free Online"
url: "http://www.crunchyroll.com/"
width: 1440
windowId: 3
__proto__: Object { … }
What gives? Why can I access the array's elements from the console but not the code?
As you can see the array pinned is populated but I can't access the individual elements inside using the array. Does anyone know why this is happening?
This is actually an issue of asynchronous code and array reference.
Let's try and see how this goes, step by step :
You call browser.tabs.query. It returns a promise, which is not resolved immediately. Therefore you don't go into the then part for the time being, so the code execution goes on to the next part...
Which is console.debug("All Pinned Tabs", pinned);. You're logging an array, so you're giving console.debug a reference to the array, which is really important here. This reference won't change over time : when you declared the pinned array and when the script ends, the array reference will still be the same.
Then you move on to console.debug("First Pinned Tab", pinned[0]);. At this point, the promise handler has not been executed yet, thererefore pinned is still empty and pinned[0] actually is undefined. That's why undefined gets logged.
The promise resolves: you go through the then handler, receive the tabs list as argument and push the tabs to your pinned array.
Remember, your first console.debug logged the array based on the array reference, that didn't change over time. When you're viewing the array in the console afterwards, the array has since been populated and what you access in the console is the array in its final state. That's why the log of the whole array does print the array itself.
Let's make sure of this
...with a little test: at step 2, instead of logging the pinned array itself, let's log the id of all elements currently in pinned.
var pinned = [];
browser.tabs.query({pinned: true})
.then(tabs => {
for (let [key, value] of tabs.entries() ) {
console.log("Object " + key, value);
pinned.push(value);
}
});
// will log an empty array since pinned tab is still empty,
// therefore, mapping tab ids will return an empty array
console.warn("All Pinned Tabs", pinned.map((t) => t.id));
// still undefined, nothing changed here
console.warn("First Pinned Tab", pinned[0]);
Here's what you should get:
All Pinned Tabs : Array []
First Pinned Tab undefined
How to deal with it?
A solution is to chain another promise at the end of the first one (which is made possible because each then handler returns a new promise, so you can chain them), like this :
var pinned = [];
browser.tabs.query({pinned: true})
.then(tabs => {
for (let [key, value] of tabs.entries() ) {
console.log("Object " + key, value);
pinned.push(value);
}
}).then( () => {
// ✓ [1,4,3,2] or anything matching your tabs ids
console.debug("All Pinned Tabs", pinned.map((t) => t.id));
// ✓ First tab object: { ... }
console.debug("First Pinned Tab", pinned[0]);
});
And now you do get the expected result:
All Pinned Tabs Array(4) [ 1, 4, 3, 2 ]
First Pinned Tab Object { id: 1, index: 0, windowId: 3, highlighted: false … }
Note that instead of manipulating a global variable for the array, you could make the first promise return an array that would be the argument for the second promise.

React emit KeyboardEvent from input

Is there a way to emit KeyboardEvent for input in React ?
I tried with:
this.input.dispatchEvent(new KeyboardEvent('keydown', {
key: 'Backspace',
bubbles: true,
cancelable: false
}));
this.input.dispatchEvent(new KeyboardEvent('keyup', {
key: 'Backspace',
bubbles: true,
cancelable: false
}));
this.input.dispatchEvent(new KeyboardEvent('keypress', {
key: 'Backspace',
bubbles: true,
cancelable: false
}));
But none of this triggers any handler (onKeyDown, onKeyPress, onKeyUp).
Handlers are called on manually key press only ...

Categories

Resources