I am using a namespace for my javascript code, and I think I have hit a brick wall with a onchange attribute for a select element. When I attempt to call a function with (or without) my namespace the error console is reporting that the function is not found.
var MYNS = {}; //namespace
MYNS.modifySearchPage = function () {
....
var eSelect = document.createElement("select")
.....
eSelect.setAttribute('onchange', 'MYNS.handleChange(this)');
.....
//set up the options (value, textcontent, eSelect.appendChild(theOption)
...
// add the eSelect to the DOM
}
MYNS.handleChange = function (select) {
//parse the select options
}
The result I get in the console when I select an item from the dropdown list is:
Uncaught ReferenceError: MYNS is not defined
I have attempted to add the namespace to the windows but that does not seem to help (and I'm not convinced that is a safe thing to do).
I have tried adding a onclick handler to the select element but obviously that is a bad idea as select does not handle onclicks.
Stripping the MYNS from both the call and function definition also didn't help.
Any ideas?
Thanks,
mwolfe
Don't use attributes to attach handlers - use properties:
eSelect.onchange = function() {
MYNS.handleChange(this);
};
More generically you could also use the standard and more recommended addEventListener:
function changeHandler() {
MYNS.handleChange(this);
}
if (eSelect.addEventListener) {
eSelect.addEventListener('change', changeHandler, false);
} else if (eSelect.attachEvent) {
eSelect.attachEvent('onchange', changeHandler); // fallback for IE
}
It's also worth noting that you can call
eSelect.addEventListener('change', MYNS.handleChange, false);
You will need to modify your callback though - the argument passed will be an event object and this inside the function will refer to the element that triggered the event.
You just code a different word of wrong case MyNS.handleChange, it should be MYNS.handleChange. In JavaScript variables are case sensitive.
Related
I create this class using pure javascript:
var SelectFeature = /*#__PURE__*/(function (Select) {
function SelectFeature() {
Select.call(this, {
condition: ol.events.condition.click
});
}
this.on('select', function (e) {
//some logic
});
if (Select) SelectFeature.__proto__ = Select;
SelectFeature.prototype = Object.create(Select && Select.prototype);
SelectFeature.prototype.constructor = Select;
return SelectFeature;
}(ol.interaction.Select));
as you can see I pass ol.interaction.Select as a parameter to the class and using Select.call() the method in SelectFeature as a constructor.
Here is a description of ol.interaction.Select class.
The ol.interaction.The select class has a member who is called getFeatures().
I try to access this method when some DOM element is clicked(this block is inside SelectFeature class):
$("#popupFeat-closer").click(function () {
this.getFeatures();
});
The code above is fired when DOM element is clicked but, on this row:
this.getFeatures();
I get this error:
Uncaught TypeError: this.getFeatures is not a function
My question how can I access getFeatures function which is located in click event handler?
Like it is mention in the comments, you have a context problem. Almost every library or framework has a method to keep the context, in jQuery you can achieve this with proxy method. Something like this should work,
$("#popupFeat-closer").click($.proxy(function () {
this.getFeatures();
}), this);
I would say that is always good to use the library/framework way of solving these, but the "old" way works too,
var self = this;
$("#popupFeat-closer").click(function () {
self.getFeatures();
});
jQuery Docs - proxy
What's a standard way of associating unique properties to anonymous JavaScript functions while creating them so that these properties can be accessed within the functions themselves when they are executed (i.e. run time)?
A scenario is this:
Say I have events which I want to bind to dynamically generated anonymous functions.
var events = ['connect','disconnect','error','connect_failed'];
for(a in events){
var handler = function(){
// console.log(arguments.callee.custom); // Not reliable 'cos callee is supposedly deprecated
console.log('<event-name>');
};
handler.custom = events[a];
$(window).on(events[a],handler);
}
Since using arguments.callee is deprecated and not guaranteed to be optimal on all platforms. What's the recommended way of achieving something similar to this?
You can use handler.custom in your function, too:
var handler = function() {
console.log(handler.custom);
console.log('<event-name>');
};
To prevent scoping issues in case of asynchronous callbacks, you can create a newly scoped handler function by wrapping your code in a closure:
for(a in events){
(function() {
var handler = function(){
console.log(handler.custom);
console.log('<event-name>');
};
handler.custom = events[a];
$(window).on(events[a],handler);
})();
}
EDIT: just realized you can use forEach as well (although that also suffers from browser compatibility issues):
events.forEach(function(event) {
var handler = function() {
...
};
handler.custom = event;
$(window).on(event,handler);
});
Named function expressions give a reference to the function itself only within the function scope:
var events = ['connect', 'disconnect', 'error', 'connect_failed'];
for (var a in events) {
var handler = function inside() {
console.log(inside.custom); // not deprecated
console.log('<event-name>');
};
handler.custom = events[a]; // now will stay with function even if renamed or moved
$(window).on(events[a], handler);
}
However, Internet Explorer 8 and below will not parse named functions correctly - Juriy Zaytsev explains the exact issue behind them in more detail: http://kangax.github.io/nfe/#named-expr
If you really have to target IE8 and below, either stick with arguments.callee or use conditional compilation for IE, which is basically IE conditional comments for JavaScript.
You could do something like this:
var events = ['connect','disconnect','error','connect_failed'];
function assignHandler(eventType, custom) {
$(window).on(events[a], function() {
console.log(custom);
});
}
for(a in events){
assignHandler(events[a], events[a]);
}
The magic of JS closures means that the anonymous function assigned as an event handler inside assignHandler() will be able to access the custom argument of assignHandler() even after assignHandler() has finished.
However, since you seem to be using jQuery's .on() method you don't need to implement anything yourself because jQuery already has functionality for this:
.on( events [, selector ] [, data ], handler(eventObject) )
Note the optional third argument is data. Within the event handler this can be accessed as event.data.
for(a in events){
var handler = function(e){
console.log(e.data);
console.log('<event-name>');
};
var custom = events[a];
$(window).on(events[a], null, custom, handler);
}
I know what you mean...
you want event name when the event is triggered and want to know which event has triggered at run time in function... Am I Right? then you can use Event Object received in handler...
DEMO
var events = ['connect','disconnect','error','connect_failed'];
for(a in events){
var handler = function(e){
console.log(e.type);
console.log('<event-name>');
};
$(window).on(events[a],handler);
}
for(a in events){
$(window).trigger(events[a]);
}
After discovering about Javascript namespaces, I tried to implement them but I run into a problem while trying to attach a namespace method to an element's onclick.
I used this method to wrap up my functions/methods/classes (a simplified concept, not my actual code):
;(function(window, undefined) {
//my namespace
var NS = {};
NS.test = {
f : function(param) {
alert(param);
}
}
NS.test.('test 2');
})(window);
Inside, everything works fine and "test 2" is prompted.
However, when I try to attach that function to a click event, by doing something like this:
<a href-"#" onclick="NS.test.f('test');">Click me!</a>
it doesn't work, just like it doesn't work when I call that function after the })(window); part.
I tried it calling it window.NS.test.f('test'); but with no effect.
How can I make an onclick event call my function?
I could attach an event listener inside my wrapper, like I do for other html elements with no difficulty, but it would be problematic in this case since I'm generating the links with javascript and I find it easier and simpler to just add onclick="doSomething" for all my links, instead of creating them, then cache them and add event listeners.
Call me lazy, but in this particular case I prefer to do
someDiv.innerHTML = my_Generated_Html_Code_With_OnClick;
instead of
//demo code, ignore the flaws and the fact it won't work on IE
someDiv.innerHTML = my_generated_Html_code;
myLink = document.getElementById(id);
myLink.addEventListener('mousedown', NS.test.f('test'));
I do not use any framework nor do I wish to, since I'm trying to get a better understanding of the so-called vanilla javascript first.
I set up a jsfiddle here.
P.S. I must admit I didn't understand namespaces completely so if I'm doing something wrong here or applying the concept in a way I am not supposed to, I would appreciate any tips or corrections
That's because NS is declared inside and hence only exists inside the function:
function(window, undefined) {
var NS = {};
// NS exists here ...
}
// ... but not here
If you want to make it available to the rest of the page, then you can do:
function(window, undefined) {
var NS = window.NS = {};
// NS and window.NS exist here ...
}
// ... and window.NS exists here.
How do I get the correct context set when I want to apply focus()?
What I try to do is basically this:
elemnt = document.getElementById('someFormField');
elemnt.focus('none');
Only the call to focus is generated somewhere else in the script, queued and applied when the application requests it.
function testIt() {
var queued = {
elementId: 'someFormField'
, func: focus
, args: ['none']};
elemnt = document.getElementById(queued.elementId);
queued.func.apply(elemnt, queued.args);
}
The above method works for other functions but for the focus method I get an error:
Opera: WRONG_THIS_ERR
Firefox: uncaught exception: [Exception... "Illegal operation on WrappedNative prototype object" (...)]
How can get this to work?
(I'm trying to understand the issue, so I'm not interested in 'use framework X' answers)
Update:
There seems to be some confusion about why I pass an argument to focus(): I only included the argument to show that passing an argument to focus() will not cause an error.
I might just as well have typed:
document.getElementById('someFormField').focus();
Update (2):
Because Anonymous's answer does not work in IE6, I'm now using an intermediate function to solve the problem:
function testIt() {
var queued = {
elementId: 'someFormField'
, func: setFocus
, args: ['someFormField']};
elemnt = document.getElementById(queued.elementId);
queued.func.apply(elemnt, queued.args);
}
function setFocus(elemntId) {
document.getElementById(elemntId).focus();
}
If IE6 compatibility is not on your requirement sheet, Anonymous's solution is the answer
In the interests of avoiding confusing myself with what function does what, I would always start with a closure, until I needed to reduce memory usage:
var dofocus = function(){element.focus()};
dofocus();
If you're in a loop and element is changing, you can use a function that returns another function: (function(e,m){return function(){e[m]()}})(element, method)
Edit: On a reread of the question, I'm thinking the reason your code doesn't work is because you're passing in the value of some variable named 'focus' and not the string method name 'focus', or rather the focus function (is it defined elsewhere?)
Edit: This works:
<html><title>test</title><script>
function foo (id, method, args) {
var d = {"id": id, "method": method, "args": args};
// ... time passes
var e = document.getElementById(d.id); // test exists
var f = e[d.method]; // test exists
var a = d.args; // test is an array
f.apply(e, a);
}
</script><body id="thebody">
<p>
<button onclick="foo('bar', 'setSelectionRange', [4,6]);">select</button>
<button onclick="foo('bar', 'focus', []);">focus</button>
<input type="text" id="bar" value="foo bar baz">
<p>
<button onclick="foo('thebody', 'appendChild', [document.createTextNode(new Date())]);">body</button>
</body></html>
Edit: And if passing functions was the issue, you can always pass an optional method name and test whether the method is a string or a function, then be able to use 'focus' or a custom function without having to place it on the element.
Read the thread here.
From that thread, we learn that using just "focus" doesn't work, like so:
focus.apply(document.getElementById('someElement'));
(a simple case of what you're attempting) because "focus" has to have an element to which it's bound. Otherwise, it's not a bound function, but rather just a native call with no real reference. My guess is this'll be the case for any element-specific functions.
What you need to do is grab the focus element from either the target element, or another DOM element that has a focus function. For example, this will work:
var fn = document.createElement('input').focus;
fn.apply( document.getElementById('someElement') );
Where SomeMethod could have:
function SomeMethod(item)
{
item.setAttribute('name', item.id);
}
Instead of:
function SomeMethod(itemId)
{
var someItem;
someItem = document.getElementById(itemId);
someItem .setAttribute('name', someItem .id);
}
Silly example, but the idea is not to send in the id itself, but the actual control calling the method. I swear this can be done but have had no luck searching... partially because I'm not even sure what to search on.
I thought it was self, but self doesn't seem to be what I want when the script I have runs.
Use the this Keyword.
You actually don't need to pass this as an argument to your function, because you've got a click event object that you can access. So:
<script>
function clickEventHandler(event) {
if (!event) {
event = window.event; // Older versions of IE use
// a global reference
// and not an argument.
};
var el = (event.target || event.srcElement); // DOM uses 'target';
// older versions of
// IE use 'srcElement'
el.setAttribute('name', el.id);
}
</script>
I tend to use this approach in all function calls from HTML attributes:-
onclick="SomeMethod.call(this)"
Then in the javascript do:-
function SomeMethod()
{
this.setAttribute('name', this.id);
}
This has a distinct advantage when you may also assign directly to event handler properties in Javascript code:-
document.getElementById("someID").onclick = SomeMethod
If SomeMethod took the context element as a parameter it would very awkward to set up:-
function(id) {
var elem = document.getElementById(id)
elem.onclick = function() { SomeMethod(elem); }
}("someID");
Worse yet this would be memory leaking closure.
At this point: SomeMethod(this) - this returns window object so do not use it. The right way to use this keyword is making it context relevant, so use SomeMethod.call(this).