Javascript simple class scope changes when button is pushed - javascript

I am sure this is one of the basic questions. I am creating a custom Javascript class and using Jquery on top to do few things. After checking if document is ready I am calling init() and adding event handler. However, when I click the button, I get inside "turnLightOn" function but when it tries to call another function for this class the scope of this has changed to the "button" so I get
Object #<HTMLButtonElement> has no method 'getDataFromServer'
I know this is a simple question but can anyone point to the right answer for me?
<div id="cp">
<ul>
<li><button id="light_switch">Light On</button></li>
</ul>
</div>
<div id="room">
</div>
<script type="text/javascript">
var CP = function(widget, light_switch){
this.widget_name = widget;
this.light_switch = light_switch;
var self = this;
console.log(this);
};
CP.prototype.init = function(){
$("#"+this.light_switch).on('click', this.turnlighton);
};
CP.prototype.somecallback = function(){
console.log(this);
};
CP.prototype.turnlighton = function(){
this.getDataFromServer(somecallback);
};
CP.prototype.getDataFromServer = function(callback){
$.ajax({url:"/"+fname,
success:function(result){
callback(result);
}
});
};
$( document ).ready(function(){
var c = new CP("control_panel", "light_switch");
c.init();
}
);
</script>

in the click handler this by default will refer the clicked element, you can pass a custom context by using Function.bind() or $.proxy()
$("#" + this.light_switch).on('click', $.proxy(this.turnlighton, this));
Demo: Fiddle

I found the issue:
$("#"+this.light_switch).on('click', this.turnlighton);
In this above code this.turnlighton will taken current this of on() method in jquery. you use CP function this.
<script type="text/javascript">
var _this;
var CP = function(widget, light_switch){
this.widget_name = widget;
this.light_switch = light_switch;
_this = this;
console.log(this);
};
CP.prototype.init = function(){
$("#"+_this.light_switch).on('click', _this.turnlighton);
};
CP.prototype.turnlighton = function(){
console.log(this);
_this.getDataFromServer();
};
CP.prototype.getDataFromServer = function(){
/*$.ajax({url:"/"+fname,success:function(result){
console.log(result);
}});*/
};
$( document ).ready(function(){
var c = new CP("control_panel", "light_switch");
c.init();
}
);
</script>
Fiddle

Related

How to use hide() in Javascript

I am trying to hide a div the id of which I stored in a variable named 'post_container_id'. My code is:
document.addEventListener('DOMContentLoaded', function() {
// Add event listener to following button
document.querySelectorAll('.post-edit').forEach(item => {
var itemid = item.getAttribute('id').slice(5,)
item.addEventListener('click', () => edit_post(itemid));
})
});
function edit_post(itemid) {
var post_container_id = `#post-container-${itemid}`;
(function(){
$(post_container_id).hide(1000);
});
};
This does not hide the div. It does not throw any error either. The function does get triggered (I checked it by logging to console). What am I doing wrong?
There is a mistake here:
(function(){
$(post_container_id).hide(1000);
});
You are just declaring the function, you should also call it:
(function(){
$(post_container_id).hide(1000);
})();
Also, the callback is useless in this case, you can just solve it as:
function edit_post(itemid) {
var post_container_id = `#post-container-${itemid}`;
$(post_container_id).hide(1000);
};
$("#hide").click(function(){
edit_post(1);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="post-container-1">secret</div>
<button id="hide">Click to hide</button>
You can also use vanilla JavaScript to hide/show the element directly by changing the style display property. As follows
function edit_post(itemid) {
const post_container_id = document.querySelectorAll(`#post-container-${itemid}`);
post_container_id.style.display = 'none';
};
Without jQuery,
document.getElementById(post_container_id).style.display = "none"

JavaScript: using THIS inside object function and binding event

I have an Object and I want to bind a function to a button when Object was initialized.
var MyClass = {
Click: function() {
var a = MyClass; // <---------------------- look here
a.Start();
},
Bind: function() {
var a = document.getElementById('MyButton');
a.addEventListener('click', this.Click, false); //<---------- and here
},
Init: function() {
this.Bind();
}
}
So, I'm new at using it and I don't know if object can be declared like this (inside Click() function that should be done after clicking a button):
Is it a bad practise? Which could be the best way in this case when adding an event here?
Edit: fiddle
Firstly you have a syntax error. getElementsById() should be getElementById() - no s. Once you fix that, what you have will work, however note that it's not really a class but an object.
If you did want to create this as a class to maintain scope of the contained methods and variables, and also create new instances, you can do it like this:
var MyClass = function() {
var _this = this;
_this.click = function() {
_this.start();
};
_this.start = function() {
console.log('Start...');
}
_this.bind = function() {
var a = document.getElementById('MyButton');
a.addEventListener('click', this.click, false);
};
_this.init = function() {
_this.bind();
};
return _this;
}
new MyClass().init();
<button id="MyButton">Click me</button>
For event listeners it's easiest and best to use jQuery, for example if you want to have some .js code executed when user clicks on a button, you could use:
https://api.jquery.com/click/
I don't know how new you are to .js, but you should look up to codecademy tutorials on JavaScript and jQuery.
.click() demo:
https://www.w3schools.com/jquery/tryit.asp?filename=tryjquery_event_click

Create click event in jquery plugin

I want to make simple plugin to show alert if class is binding by a jquery plugin.
below is my html code
<html>
<head>
<script src="js/modal-load.js"></script>
<script>
$(document).ready(function(){
$('.modal-link').modalLoad();
})
</script>
</head>
....
<body>
<div class="hblock-1 text-4 text__uppercase color-7">
<a class="login btn btn-primary modal-link" href="/login-modal.php" data-toggle="modal" data-target="#login-modal">Log in</a>
Register
</div>
</body>
This is my plugin script
(function($){
$.modalLoad = function(element, options){
var defaults = {
foo: 'bar',
onFoo: function() {}
}
var plugin = this;
plugin.settings = {};
var $element = $(element),
element = element;
plugin.init = function(){
plugin.settings = $.extend({},defaults, options);
plugin.add_bindings();
}
plugin.add_bindings = function(){
this.click(function(){
alert('a');
})
}
plugin.create_modal = function(){
$('body').append('<div class="modal-wrapper"></div>');
}
plugin.init();
}
$.fn.modalLoad = function(options){
return this.each(function(){
if(undefined == $(this).data('modalLoad')){
var plugin = new $.modalLoad(this, options);
$(this).data('modalLoad', plugin);
}
});
}
})(jQuery);
In html code, i've initialized modalLoad plugin. But when particular class that i've bind, it won't be triggered by click event.
What's wrong with my code? is any mistake with my DOM selector in add_bindings part?
Thanks for advance
*edited
You need to attach the click handler to $element not this. Inside add_bindings, this refers to the plugin object not the clicked element so this will not have a method named on(You should get an error like Uncaught TypeError: undefined is not a function in your console)
plugin.add_bindings = function () {
$element.click(function (e) {
alert('a');
e.preventDefault()
})
}
Demo: Fiddle

How to write a jquery plugin in OO way

i have written following jquery plugin. what i am trying to do is, when the user click on a link make the relevant div display: block base on the data attribute. But this plugin does not work. i have bn trying to figure this out for last two days. But i failed.
My HTML
<div class="container1">
asd
<div class="window1" data-window="a">
asd
</div>
</div>
<hr>
<div class="container2">
asdf1
asdf2
asdf3
<div class="window2" data-window="b">
asdf1
</div>
<div class="window2" data-window="c">
asdf2
</div>
<div class="window2" data-window="d">
asdf3
</div>
</div>
<script src="jquery-1.11.0.min.js"></script>
<script src="script.js"></script>
<script>
$('.container1').myPlugin({
link: $('.link1'),
container : $('.window1')
});
$('.container2').myPlugin({
link: $('.link2'),
container : $('.window2')
});
</script>
plugin
(function ($, window, document, undefind) {
MyPlugin = {
init : function (options, element) {
$.fn.myPlugin.config = $.extend({}, $.fn.myPlugin.config, options);
var link = $.fn.myPlugin.config.link;
link.on('click', this.secondFunc);
},
secondFunc : function () {
var dataLinkId = $(this).data('link'),
container = $($.fn.myPlugin.config).filter('[data-section="' + dataLinkId + '"]');
container.show();
}
};
$.fn.myPlugin = function(options) {
return this.each(function () {
var rezG = Object.create(MyPlugin);
rezG.init(options, this);
});
};
$.fn.myPlugin.config = {
link: $('.link'),
container : $('.container')
};
})(jQuery, window, document);
CSS
.window1, .window2 {
display: none;
}
DEMO
You need to use var to make sure your variables are all local and not global.
var MyPlugin = {
// ...
};
Also, in the init function, you are doing this:
$.fn.myPlugin.config = $.extend({}, $.fn.myPlugin.config, options);
This is overwriting $.fn.myPlugin.config which is the default options. This means that all elements that call myPlugin() will use the same config. You need to set the config on just the one instance.
this.config = $.extend({}, $.fn.myPlugin.config, options);
Your secondFunc doesn't have a reference to the object (rezG) instance, so it cannot access the config. You need to pass that to secondFunc(). One way is to use a closure to capture the instance.
secondFunc: function (rezG) {
return function(){
var dataLinkId = $(this).data('link'),
container = $(rezG.config.container).filter(function(){
return $(this).data('window') == dataLinkId;
});
container.show();
};
}
Then you bind it like so:
link.on('click', this.secondFunc(this));
Note that in secondFunc, you need to use config.container(not just config which is the object), and also your attribute is data-window, not data-section.
Updated demo: http://jsfiddle.net/K82gg/7/
Your plugin could be as simple as
(function ($, window, document, undefind) {
$.fn.myPlugin = function(options) {
// When $(stuff).myPlugin(...) is called
// this keyword inside of myPlugin function is referencing a set
// of elements plugin was called upon
// e.g. for call like $('.container1').myPlugin();
// this keyword will reference all elements selected by
// $('.container1') not jquery wrapped,
// in general it can be a any number.
return this.each(function pluginImplementation () {
// Here we iterate over the set, and for each element in the set
// we do some pretty standard click
var container = $(this);
// I use 'click.myPlugin' event instead just 'click' ale to later on
// do $(..).off('click.myPlugin') to remove only handlers that were
// attached by plugin (a good practice)
container.on('click.myPlugin', options.linkSelector, function(){
var dataLinkId = $(this).data('link');
container.find('[data-window="' + dataLinkId + '"]').toggle();
})
});
};
})(jQuery, window, document);
See the jsfiddle
However the code above may have a problem luginImplementation () function is created on each iteration and if the body of that function would be something more complicated it would be a mess. That is why it's better to create pluginImplementation () outside.
(function ($, window, document, undefind) {
// Notice that pluginImplementation () now accepts parameters
// They make it possible for pluginImplementation to know which
// elements it's working with
function pluginImplementation (container, options) {
container.on('click.myPlugin', options.linkSelector, function(){
var dataLinkId = $(this).data('link');
container.find('[data-window="' + dataLinkId + '"]').toggle();
})
}
$.fn.myPlugin = function(options) {
return this.each(function () {
pluginImplementation($(this), options);
});
};
})(jQuery, window, document);
The demo
That separation may be not good enough. You may want your plugin to be more OOP and what not. So you can go all OOPy like that:
(function ($, window, document, undefind) {
// For that purpose we create a class
// That describes behavior that our plugin provides
//
function MyPlugin(container, options) {
this.container = container;
this.options = options;
// To the topic of maintainability
// This could be parametrised as an option at plugin instantiation
this.eventName = 'click.myPlugin';
}
MyPlugin.prototype.attachClickHandlers = function() {
var self = this;
// This gets a little messy with all the thises vs selfs and a
// noname function wrapping the handler.
// The point is to preserve this keyword reference
// inside of clickHandler method.
// If I would have just self.clickHandler as a handler there
// this keyword inside of self.clickHandler would reference to
// whatever $(...).on binds handlers to i.e. triggering element.
// I need this keyword inside of self.clickHandler to point to
// "current" instance of MyPlugin, that's why I have wrapping
// function. It just lets me call clickHandler in the right context.
// clickHandler method also needs to know what link is being clicked
// so we pass that in as parameter.
self.container.on(self.eventName,
self.options.linkSelector,
function() {
self.clickHandler($(this));
})
}
MyPlugin.prototype.clickHandler = function(clickedLink) {
var dataLinkId = clickedLink.data('link');
this.container.find('[data-window="' + dataLinkId + '"]').toggle();
}
$.fn.myPlugin = function(options) {
return this.each(function () {
var pluginInstance = new MyPlugin($(this), options);
pluginInstance.attachClickHandlers();
});
};
})(jQuery, window, document);
In this implementation MyPlugin is a class (in javascript sense of the word class) which enables you to tackle each specific point in the way it behaves. and introduce all sorts of OOP features.
The demo

Using "this" from event handler in jquery

I have the following javascript code found also in this fiddle: http://jsfiddle.net/periklis/k4u4c/
<button id = "element_id" class = "myclass">Click me</button>
<script>
$(document).ready(function() {
this.myfunc = function() {
console.log('Hello world');
}
this.myfunc();
$('#element_id').select('.myclass').bind('click', function() {
this.myfunc(); //Obviously this doesn't work
});
});
</script>​
How can I call this.myfunc() when the element is clicked? I don't want to define the myfunc() in the global space.
Thanks as always
Create a local variable that references to the function, that way it is accessible from the anonymous function and you don't end up with myfunc in the global namespace.
<button id = "element_id" class = "myclass">Click me</button>
<script>
$(document).ready(function() {
var myfunc = function() {
console.log('Hello world');
}
myfunc();
$('#element_id').select('.myclass').bind('click', function() {
myfunc(); // works!
});
});
</script>​
If you, on the other hand, assign var that = this;, then your method myfunc will be stored on the HTMLDocument object (from $(document)), which is perhaps not what you want. But if that's what you want, then you do this (as others have suggested also, I might add).
<button id = "element_id" class = "myclass">Click me</button>
<script>
$(document).ready(function() {
// storing reference to $(document) in local variable
var that = this;
// adding myfunc on to the document object
that.myfunc = function() {
console.log('Hello world');
}
that.myfunc();
$('#element_id').select('.myclass').bind('click', function() {
that.myfunc(); // works!
});
});
</script>​
// Simon A
You could do
$(document).ready(function() {
var that = this;
that.myfunc = function() {
console.log('Hello world');
}
that.myfunc();
$('#element_id').select('.myclass').bind('click', function() {
that.myfunc();
});
});
In this way you cache the this variable with something that you can reuse in your event handlers where this points to the current element
You may be a little confused by what you are doing with the this.myfunc call.
In that context this is referring to document which means you are globally defining that function and it can be referenced at any time by document.myfunc();
If you are just wanting to put a function in a variable temporarily then the following code should help:
$(document).ready(function() {
this.myfunc = function() {
alert('Hello world');
};
var otherfunc = function() {
alert('Hi world');
};
$('.cv1').click(document.myfunc);
$('.cv2').click(otherfunc);
});
JSFiddle: http://jsfiddle.net/LKmuX/
This demonstrates both what you are doing in terms of attaching a function to document and also just putting it in a variable.
An alternative to caching the context, if you need to use the external context inside the binded function, is to use the proxy() method (docs here) to change the scope of the internal function like this :
$('#element_id').select('.myclass').bind('click', $.proxy(function() {
this.myfunc();
}, this));
In this way, I force the actual this (the context when I'm using the bind method) to be the same inside the binded function (that normally has his own context)
http://jsfiddle.net/k4u4c/2/
FYI - the same method can be found in Dojo library (in Dojo it's
largely used), and it's called hitch
Most common way to do this is to cache this in other variable and later on in handler refer to that variable as you would do with this. http://jsfiddle.net/k4u4c/3/
<button id = "element_id" class = "myclass">Click me</button>
<script>
$(document).ready(function() {
this.myfunc = function() {
console.log('Hello world');
}
this.myfunc();
var that = this;
$('#element_id').select('.myclass').bind('click', function() {
that.myfunc(); //Obviously this DOES work :)
});
});
</script>​

Categories

Resources