Event triggering inside prototype - javascript

When I try to call "Test" function I get an error.
How to fix that? (no jquery!)
Browser:firefox
error:
TypeError: this.Test is not a function
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Untitled Document</title>
<script type="text/javascript">
MyClass = function(){
}
MyClass.prototype = {
Init: function(){
var txt = document.getElementById("text");
if (txt.addEventListener) {
txt.addEventListener("keyup", this.Foo, true)
}
},
Foo: function(){
this.Test();
},
Test: function(){
alert('OK');
}
}
window.onload = function(){
obj = new MyClass;
obj.Init();
}
</script>
</head>
<body>
<textarea id="text" rows="10">
</textarea>
</div>
</body>

It's because you reference this.Foo as the event, what actually happens is that it copies that function out of the object scope, ergo this does not exist. What most people do is use an anonymous function / wrapper around the event.

Related

AngularJS 1 events - "this" variable in callback method is the window

I'm trying to learn to use AngularJS 1.6 events.
I created a component which is registered to the event:
'use strict';
var module = angular.module("Test", []);
module.controller("Controller1", Controller1);
Controller1.$inject = ["$scope"];
function Controller1($scope) {
var self = this;
self.try = function () {
$scope.$broadcast("test:try");
}
}
module.controller("Controller2", Controller2);
module.component("compo2", {
controller: "Controller2",
});
Controller2.$inject = ["$scope"];
function Controller2($scope) {
var self = this;
this.$onInit = function () {
$scope.$on("test:try", self.try);
}
self.try = function (event, data) {
console.log("Logged event!!!")
}
}
and this is my html file:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script src="./Test/angular.js"></script>
<script src="./compoTest.js"></script>
</head>
<body ng-app="Test" ng-controller="Controller1 as ctrl1">
<compo2> T.E.S.T </compo2>
<button ng-click="ctrl1.try();"> Try! </button>
</body>
</html>
But when the callback "try" is called - I see that the this variable is the window instead of my component's controller.
What am I doing wrong?
This is what happens when object method is called as a callback in JS.
To avoid accidental use of window, use strict mode in the beginning of JS file:
'use strict';
The method should be bound to the context:
this.try = function (event, data) {
this....
}.bind(this);
Or self recipe should be used:
var self = this;
self.try = function (event, data) {
self....
};

Backbone event callback bind

I am trying to bind a user defined callback as Backbone's click event.
var View = Backbone.View.extend({
events: {
'click': 'testClick'
},
tagName: "li",
attributes: {
class: "item"
},
initialize: function() {
this.render();
},
testClick: function(){
},
render: function() {
$("#container").append(this.$el.html("Click Me!!"));
}
});
function Item() {
var _view = View.extend({
testClick: this.testClick.bind(this)
});
this.view = new _view();
}
Item.prototype = {
testClick: function() {
alert("testClick from prototype called!");
}
};
var myItem = new Item();
myItem.testClick = function() {
alert("testClick from instance called!");
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone-min.js"></script>
</head>
<body>
<div id="container"></div>
</body>
</html>
Clicking on "Click me", it alerts "testClick from prototype called!"
I am not sure why the alert from the instance is not getting called. What am I doing wrong here? Please help!
Because the following line:
testClick: this.testClick.bind(this)
detaches the testClick function member of the Item instance. You are essentially reusing a function and there is no linkage between the 2 methods.
Consider this example:
var obj = {
foo: function() {
console.log('I was foo!');
}
}
var detachedFoo = obj.foo;
obj.foo = function() {
console.log('The new foo!');
}
obj.foo === detachedFoo // false
obj.foo() // logs 'The new foo!'
deatchedFoo(); // logs 'I was foo!'
If you use the following syntax the alert will show "testClick from instance called!".
testClick: () => {
this.testClick();
}
This is because the above code calls the current .testClick method of the Item instance.

How can each instance of a jQuery plugin have its own properties and methods?

I am learning how to create jQuery plugins and have built one using module pattern. It works if I apply it only once, however, if I apply multiple times, all of them get initialized using the last one's settings.
For instance, if I first do $('#div1').myPlugin();, and then later $('#div2').myPlugin({myProperty :'mydiv2Property'});, $('#div1') myProperty is changed from myDefaultProperty to mydiv2Property. The same thing happens when initializing with a different method.
I have a working (well, almost working!) example located at http://jsbin.com/eWePoro/1/, and my full script is listed below.
How do I change this script so each time the plugin is applied, it uses just its own properties and methods? Thank you
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />
<title>Testing</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.js" type="text/javascript"></script>
<style type="text/css">
</style>
<script type="text/javascript">
(function($){
var defaults = {
myProperty :'myDefaultProperty',
myMethod1 :function () {
console.log('myMethod1',this,this.myProperty);
},
myMethod2 :function () {
console.log('myMethod2',this,this.myProperty);
}
};
var methods = {
init : function (options) {
var settings = $.extend(defaults, options || {});
settings.myMethod1();
return this.each(function () {
$(this).click(function(e) {
settings.myMethod2();
});
});
},
destroy : function () {
//Anything else I should do here?
delete settings;
return this.each(function () {});
}
};
$.fn.myPlugin = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || ! method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.myPlugin');
}
};
}(jQuery)
);
$(function(){
$('#div1').myPlugin();
$('#div2').myPlugin({
myProperty :'mydiv2Property'
});
$('#div3').myPlugin({
myMethod1 :function () {console.log('myMethod1_new',this,this.myProperty);}
});
$('#div4').myPlugin({
myMethod2 :function () {console.log('myMethod2_new',this,this.myProperty);}
});
});
</script>
</head>
<body>
<div id='div1'>div1</div>
<div id='div2'>div2</div>
<div id='div3'>div3</div>
<div id='div4'>div4</div>
</body>
</html>
The problem is here:
var settings = $.extend(defaults, options || {});
You are actually modifying defaults here with new properties. So next time you run the same code, defaults will be mutated. You should probably do:
var settings = $.extend({}, defaults, options);
This will create a new settings object every time by cloning defaults before extending it.
DEMO: http://jsbin.com/eWePoro/2

this.style.backgroundColor don't work in IE7/8

my code is:
<!DOCTYPE html>
<html>
<head>
<title>Demo</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
p{
border:1px solid #CCC;
margin:5px;
padding:5px;
}
</style>
<script type="text/javascript">
window.onload = changeColor;
function changeColor() {
for(var i =0; i < document.getElementById("main").getElementsByTagName("p").length; i++) {
var obj = document.getElementById("main").getElementsByTagName("p")[i];
if (window.addEventListener) {
obj.addEventListener('mousemove', function () {
this.style.backgroundColor ="#EEE";
}, false);
obj.addEventListener('mouseout', function () {
this.style.backgroundColor ="#FFF";
}, false);
} else if (window.attachEvent) {
//for ie
obj.attachEvent('onmousemove', function () {
this.style.backgroundColor ="#EEE";
});
obj.attachEvent('onmouseout', function () {
this.style.backgroundColor ="#FFF";
});
}
}
}
</script>
</head>
<body>
<div>
<p>1</p>
<div id="main">
<p>2.1</p>
<p>2.2</p>
<p>2.3</p>
</div>
</div>
</body>
</html>
it work well in Chrome态FireFox and ie9,but can't work in IE7/8
the error message is:Unable to set the property value of the "backgroundColor": the object is null or undefined
what's rong with me?
When using attachEvent in IE, this is set to the window object, not to the object that the event happened on.
In IE, the global variable window.event.srcElement will contain the target object for the event.
You could code a work-around like this to make all the event handlers work the same:
function hookEvent(event, obj, fn) {
if (obj.addEventListener) {
obj.addEventListener(event, fn, false);
} else {
obj.attachEvent("on" + event, function() {return(fn.call(obj, window.event));});
}
}
This will make it so that this is set to the source object of the event and that the argument to the event handler is the event object.
this is not bound to the source element in IE's attachEvent.
Use event.srcElement instead.
Also note that the event global object property and its srcElement property are IE-specific as well.

HTA Javascript - why is object out of scope in onbeforeunload?

This is a bit of a strange one. I have been trying to call a function in a child object from the parent object but it seems to be going out of scope in onbeforeunload function. These function calls work outside of a onbeforeunload, so it only fails when called in onbeforeunload. I can fix it by making the child object a global but I was trying to avoid that. Anyone know why my childobject is out of scope when called in onbeforeunload? Notice I call that same function in windows onload event and it works fine. Here is the code:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Broken HTA</title>
<HTA:APPLICATION
ID="Broken HTA"
APPLICATIONNAME="Broken HTA"
BORDERSTYLE = "flat"
CAPTION="Yes"
CONTEXTMENU = "no"
INNERBORDER = "no"
MAXIMIZEBUTTON = "no"
MINIMIZEBUTTON = "yes"
NAVIGABLE = "No"
SCROLL = "auto"
SCROLL FLAT = "Yes"
SELECTION="no"
SYSMENU="yes"
SHOWINTASKBAR="yes"
SINGLEINSTANCE="yes"
VERSION = "1.0"
BORDER="thick"
>
<script language="javascript" type="text/javascript">
var myparent;
function windowLaunch()
{
myparent = new parent();
myparent.getchildvalue();
window.onbeforeunload = myparent.getchildvalue;
window.resizeTo(500, 610);
}
function parent()
{
this.mychild = new childobject("myinput");
this.getchildvalue = function()
{
var tmpval = this.mychild.returnvalue();
};
}
function childobject(thename)
{
this.myprop = thename;
this.returnvalue = function()
{
return (document.getElementById(this.myprop).value);
};
}
</script>
</head>
<body id="thebody" onload="windowLaunch();">
<div id="outerdiv">
<span title="This Input Box">My Input:</span><br />
<input id="myinput" style="width: 290px"/>
</div>
</body>
</html>
this is based on how a function is called. Calling myparent.getchildvalue() is fine, but as soon as you assign myparent.getchildvalue as a handler, it will be called out of context. You can demonstrate this simply:
var obj = { val: 42, fn: function(){ alert(this.val) } };
ref = obj.fn;
alert(obj.fn === ref); // true, so expect the following to do the same thing...
obj.fn(); // 42, so far so good
ref(); // undefined. uh oh...
You can get around this by wrapping:
...
window.onbeforeunload = function(){ myparent.getchildvalue(); };
...
or binding:
...
window.onbeforeunload = myparent.getchildvalue.bind(myparent);
...

Categories

Resources