I'm trying to record every time a user changes a text input on my web page. I'm using the following:
function formInit(socketObject) {
$('input:text').change( (eventObject) => {
console.log(document.domain);
console.log(JSON.stringify($(this)));
//... send stuff via socketObject
});
}
which I'm calling from $(document).ready(). I'm getting the following error on that fourth line, the one that logs $(this):
SecurityError: Permission denied to access property "toJSON" on cross-origin object
despite the fact that console.log(document.domain); returns localhost from both the script and from the master document it's being called from. The only thing that isn't being served up from my flask server on localhost are a couple of libraries (jquery, socketio, cleave) and possibly some components in a bokeh chart, which I'm pretty sure don't qualify as input:text, and certainly haven't been the thing I've been .change()ing in my debugging.
Can anyone suggest where I'm going wrong here, or where I should be looking? Thanks.
The error is because this is not the input:text element as you seem to be expecting, because you're using an arrow function, so lexical scope is not maintained. You either need to change the arrow function to an anonymous function:
function formInit(socketObject) {
$('input:text').change(function() {
// 'this' now refers to the element which raised the event
});
}
Alternatively keep the arrow function, but get a reference to the element from the event which is raised and provided as an argument to the handler function:
function formInit(socketObject) {
$('input:text').change(e => {
// 'e.target' now refers to the element which raised the event
});
}
Aside from the error, stringifying an entire jQuery object is a little odd. It's a better idea to just pull out only the relevant properties you need, something like this:
function formInit(socketObject) {
$('input:text').change((e) => {
console.log(document.domain);
console.log({
id: e.target.id,
value: e.target.value
// other properies here...
});
// send stuff via socketObject...
});
}
Related
In Javascript, if a function is referenced that doesn't exist, I can catch the resulting error using:
window.onerror = function(msg, src) {
// insert logic here
return true;
}
By parsing the content of msg, I can determine what function the developer was trying to call, and then by creating a Proxy object, I can prepare the app to ingest the arguments passed to the non-existent function that was referenced should the same function be called again.
However, it'd be ideal if I could handle the reference the first time it occurs. I'm interested in knowing if there's a way to either capture the arguments in the error handler, or if it's possible to declare a "wildcard" function that could respond when an undefined function is referenced.
Does such a thing or methodology exist?
Thank you in advance for your consideration and time.
I'm creating an extension in chrome for the first time (I'm not a web, or javascript developer). I'm adding onto a codebase that is in an older version of javascript that I've never used (Once I'm at a computer with that codebase, I'll tag which version it is, but I can't remember).
I have a class called DownloadManager, and inside of it I am calling chrome.downloads.onChanged, and within it, I call another function inside of the class, but it can't recognize the class (I think that's the issue).
// Class named DownloadManager
function DownloadManager(someData) {
this._myData = someData;
// function that does a thing, and tests run successfully
this.doAThing = function(someData) {
// Code in here that we assume works, and there's no issues.
}
if(chrome.downloads) {
chrome.downloads.onChanged.addListener(function(delta) {
// Error here
this.doAThing(delta.data);
}
}
}
The error I'm getting is on the this.doAThing(this._myData); line. The error is Error in event handler for downloads.onChanged: TypeError: Cannot read property 'doAThing' of null at <URL>.
I'm assuming it's a scoping issue, and this. doesn't mean anything there, and it can't access doAThing there. I'm certain that the argument taken in is of the same type as the function declared above.
I'll add more data when I'm back in that environment.
Inside your event handler for chrome.downloads.onChanged the this keyword now has a different context than this inside the DownloadManager. It might make sense that since you defined the event handler within the downloadManager that you could share the variable, but that just happens to be a coincidence of "where the code was defined vs where the code is invoked from".
You could probably get away with assigning this to a variable in the main scope:
function DownloadManager(someData) {
this.doAThing = function(someData) {
// Code in here that we assume works, and there's no issues.
}
window.myScope = this;
if(chrome.downloads) {
chrome.downloads.onChanged.addListener(function(delta) {
// Error here
window.myScope.doAThing(delta.data);
}
}
}
I have a search input field to receive user input and a variable-state button that responds to user input. Notice the on-input event on the search input.
<dom-module id="app-search">
<template>
<input type="search" id="searchInput" on-input="_onInput" />
<!-- NOTE: button is set to 'disabled' when there is no value of search field -->
<paper-button disabled>Done</paper-button>
</template>
</dom-module>
In the Polymer ready() definition I get a handle to the paper-button element (The ready() function is called after everything, including after the element and its properties have been initialized, so it's a good time to query the local DOM).
ready() {
super.ready(); // must call this for Polymer to work
// get handle to paper-button element
this._doneBtn = Polymer.dom(this.root).querySelector('paper-button');
}
(By the way, I know using this.$ can be used as a syntax short-cut for Polymer.dom(this.root).querySelector() but that syntax only seems to work for targeting elements in the local DOM that have an id, e.g: this.$.searchInput will return a handle to the element with id="searchInput". Does anyone know of a shorthand for targeting non-id, regular elements without having to type Polymer.dom(this.root)...?)
I have a function that detects input events on the search field. If there is a value in the search field, enable the button.
_onInput() {
// if search input has value
if (Boolean(this.$.searchInput.value)) {
// remove disabled attr from button
this._doneBtn.removeAttribute('disabled');
} else {
this._disableBtn();
}
}
_disableBtn() {
this._doneBtn.setAttribute('disabled', true);
}
Up to now, this works so that when a user starts typing, the button becomes enabled; when there is no value the button becomes disabled.
However, by convention, users can also delete the input value by clicking the little 'x' that appears on the right-hand-side of the search input. Developers can detect that event by attaching a search event to the input element:
ready() {
super.ready(); // must call this for Polymer to work
// get handle to paper-button element
this._doneBtn = Polymer.dom(this.root).querySelector('paper-button');
// attach 'search' event listener to search input field
// call the element's '_disableBtn' function
this.$.searchInput.addEventListener('search', this._disableBtn);
}
The problem is when I trigger the event by clicking the 'x' that appears when the search field has a value, the this._disableBtn function fires, but this._doneBtn inside the function returns undefined:
Uncaught TypeError: Cannot read property 'setAttribute' of undefined.
Assuming this might have to do with an improper type definition, I tried declaring the _doneBtn property in the Polymer properties getter:
static get properties() {
return {
_doneBtn: Object // also tried 'String'
}
}
I also tried querying the DOM from inside the _disabledBtn function again and trying to re-declare the property but I still get the same error:
_disableBtn() {
if (!this._doneBtn) {
this._doneBtn = Polymer.dom(this.root).querySelector('paper-button');
}
this._doneBtn.setAttribute('disabled', true);
}
Can anyone understand what's happening here? This seems to have something to do with the event listener. Perhaps the DOM is not fully rendered before it's parsed although switching the order of the declarations in the ready() call doesn't make a difference? It could also have something to do with this.
Interestingly, when I console.log(this) inside _disableBtn, the console returns two different this instances, one for the host element (<app-search>) and one for the target element that fired the event: two elements point to 'this'. Also noteworthy is the order that this is printed.
I'm hoping someone wiser than me can help solve what's going on here.
My gut feeling is that you don't use the "this" correctly. The "this" expresion is counter-intuitive as we tend to automatically associate it with the lexical scope or the location where the "this" is used. Alas, this is not the case as the "this" binding is not determine at JIT "compile time" nor a "runtime" but at "call time".
The this represents the context it was called from. So by simply moving a line of code from one function to another, the "this" could refer to something else. And that something else may not have a "setAttribute" function attached thus the "of undefined" error message.
One way to workaround this (no pun intended) reality is to configure the called function with a .bind([value we want "this" to have when calling that function]).
For instance, instead of writing the following snippet:
} else {
this._disableBtn();
}
you would have writen
} else {
this._disableBtn().bind(this);
}
You would have made shure that the "this" you use in the _disableBtn() function woud point to the same target as the "this" does in the "else" block.
Basically, the "this" value depends on the original call site context the function it is used in was called (I'm not sure if this is clear... but reread it slowly if it doesn't make sense on the first flight). To hardcode a value of "this" in the called function, you can add the .bind("this" value you want to pass to the called function) when calling the function such as:
function aFunction() {
myfunction(parameter).bind("required value of 'this' in myFunction").
}
Hope this helps.
Jake
After reading #softjake's response, I was able to solve the problem.
First, let's revisit the addEventListener() setup that was added in the ready() function:
ready() {
super.ready();
// handle to toggle button element
this._doneBtn = Polymer.dom(this.root).querySelector('paper-button');
// previous method === no worky
//this.$.searchInput.addEventListener('search', this._disableBtn);
// changed it to:
this.$.searchInput.addEventListener('search', this._disableBtn.bind(this._doneBtn));
}
The key here is that I'm using .bind(this._doneBtn) to make sure this, inside the scope of the _disableBtn function refers to this._doneBtn and not some other this (like the parent element or document/window).
Finally, adjust the _disableBtn function slightly:
_disableBtn() {
// previous (also no worky)
//this._doneBtn.setAttribute('disabled', true);
// changed to:
this.setAttribute('disabled', true);
}
Because this already refers to this._doneBtn, we can simply use this.setAttribute(...).
Coming from a C++ background, trying to work with an OO language that doesn't have explicit typing is a little more than a headache.
So I have dynamic elements for a webpage that are "controlled" by objects since there are tons of stuff I need to manage on each for it to work. The element is just the visual output of the data inside of the object itself, that's all I really need it for.
Except that I need the object to perform an internal function when it's clicked. That seems to be the biggest source of my headache thus far.
Javascript:
function onClick(file) //The external onClick function I use to try to get it to call from.
{
file.state = INUSE;
file.checkState();
}
function fileObject () { //The file object itself
this.element;
this.newElement();
//initialize stuff for the object
}
fileObject.prototype.newElement = function() { //creates a new element and sets its event listener
this.element.click(function() {onClick(this)});
}
fileObject.prototype.checkState = function() {/*does stuff*/} //apparently this is "not a function"
The error I get exactly is "file.checkState is not a function" from Firefox's console panel.
I'm still new to javascript, but after doing some debugging, I've come to find out that it's explicitly the onClick(this) function that is causing all of the errors. When used with something else, the onClick function works perfectly, but for some reason, the this keyword doesn't appear to actually be sending the reference to the fileObject since all checks show file being undefined when inside of the onClick scope.
Is there something fundamentally wrong about the way I'm trying to do this or am I just missing a step (or adding something that I don't need) that will help get this snippet working.
So you know, your initial problem isn't actually handling the action, but listening to it. click will trigger a synthetic click event, rather than liste for one.
You want ... .element.addEventListener("click", callback); that said, you face a second problem, immediately thereafter.
I will leave my example code as you've written it to not confuse the matter...
But when you see click( ) know that I mean subscribing with addEventListener, if element really does mean a browser DOM element. If it's not a standard browser element, and your own API, then ignore the previous portion, and carry on.
this is dynamically bound at the invocation time of the function (not at definition time).
The nearest function, scoped above, is your callback function that you are passing into .click( ... ).
Which is entirely different than the this which you mean outside of the callback.
Whatever is on the left-hand side of the dot is the this context for the duration of that particular invocation.
Needless to say, click() doesn't know enough to bind the this you mean, to the left-hand side of your callback.
The solution (or one of many) is to use lexical scoping and/or closure to retain the value of the object you mean.
// easy but messier
var fileObject = this;
... .click(function () { onClick(fileObject); });
// Cleaner with thunks:
function clickHandler (onClick, obj) {
return function () { onClick(obj); };
}
... .click(clickHandler(this));
Coming from c++ the way Javascript handles this will seem a little crazy, it looks like here you need to tell the function you've defined what this is - like so:
this.element.click(function() {onClick(this)}.bind(this));
I am new to JSONP and had implemented cross domain functionality for my application and everything is working fine. Now i want to change my javascript code to apply object orientation.
My api is
http://localhost:8080/myApplication/getComments?callback=displayComments
CrossDomain.prototype.displayComments = function(data) {
// code to display the comments
}
Now I am getting an error in firebug given below
ReferenceError: displayComments is not defined
I changed the api to
http://localhost:8080/myApplication/getComments?callback=this.displayComments
and found that the function is appended inline to the callback like this
http://localhost:8080/myApplication/getComments?callback=callback=function (jsonData)
{
//code to display the comments
}
this time another error in firebug
SyntaxError: function statement requires a name
I have a doubt whether to use JSONP in object oriented javascript or not.
Please help.
Thanks in advance.
There's no point in defining the function on the prototype of a function unless you are going to create instances of that function, so start by doing that.
var myCrossDomain = new CrossDomain();
Then you have to call the method on the object, not as a global (it isn't a global, so you can't do that anyway)
var uri = "http://localhost:8080/myApplication/getComments?callback=" +
encodeURIComponent("myCrossDomain.displayComments");
In response to edits and comments:
Yes i am creating an instance of this in another js file
Then reference it as shown above.
I changed the api to
http://localhost:8080/myApplication/getComments?callback=this.displayComments
It's JSON-P. It runs by adding a new script element. Everything gets called in the global context. That is going to call this.displayComments which will be the same as window.displayComments.
If you want to call your method directly, then you need to specify the global variable holding the instance explicitly.
If you don't want to call it directly then you can use the more traditional approach of generating a new, anonymous function which has access to said object through a closure, assigning that function to a global variable (with a unique name) and using that name as your callback argument.
and found that the function is appended inline to the callback like this
http://localhost:8080/myApplication/getComments?callback=callback=function (jsonData)
You haven't shown us the code that creates the URI so we can't tell why that might be the case.