Mootools Element.prototype error - javascript

I want to implement some functions and variables into Element member of mootools. I have something like this
Element.prototype.currentChild = this.getFirst();
Element.prototype.scrollToNext = function(delta, tag){ .... }
After that I create a new element and bind the mousewheel event to a span and acces it's currentChild.
body_container = new Element('div', {
events:{
'mousewheel': function(e){
var elem = new Element(this);
elem.currentChild.setStyle('background-color', 'transparent');
elem.scrollToNext(e.wheel);
elem.currentChild.setStyle('background-color', '#C6E2FF');
e.stop();
}
}
});
The problem is I get the following error:
Uncaught TypeError: Object [object Window] has no method 'getFirst'
Do you know what might cause this?
LE: Yes, I was expecting 'this' to be an Element. But I can't see why it would be Window type.

use Implement to change the prototype. and you will need a function, can't say something.prototype.method = this.somethingsMethod as this is not bound outside of the execution context of the method.
Element.implement({
currentChild: function() {
return this.getFirst();
},
scrollToNext: function() {}
});
MooTools also has alias.
Element.alias('currentChild', 'getFirst');
https://github.com/mootools/mootools-core/blob/master/Source/Core/Core.js#L223-225 - aliasing on type methods, when you don't want to re-implement.
to be honest, why can't you just use element storage instead?
'mousewheel': function(e) {
var elem = document.id(this),
first = elem.retrieve('currentChild');
first || elem.store('currentChild', first = elem.getFirst());
first.setStyle('background-color', 'transparent');
elem.scrollToNext(e.wheel);
first.setStyle('background-color', '#C6E2FF');
e.stop();
}

Thanks for the quick answers. Meanwhile i found a method based on Dimitar first solution with implement. It looks like this:
Element.implement({
currentChild: function(){
if(!this._currentChild) this._currentChild = this.getFirst();
return this._currentChild;
}
}

Related

Javascript: run object method on DOM object selected through other property

I am very new to javascript.
Here I am failing to run an object method on a DOM element that I selected through another property of the same object. I suspect there is something wrong with my thinking!
Thanks in advance for any piece of help.
var Arrow = function() {
this.current = $('.arrow');
this.previous = null;
this.bend = function() {
// do bend
};
};
var arrow = new Arrow();
arrow.current.bend();
bend() is a method of Arrow, not current. Use arrow.bend() and it will also have access to current using this.current.
arrow.current.bend is not defined.
You have defined:
this.current as the Array of DOM elements.
this.bend as method with a function.
Hence, you can call:
arrow.current >> returns Array of DOMs
arrow.bend() >> executes function bend.
arrow.current.bend() does not exist.
Also, note that arrow.current is an array. You'd first need to get each of the elements:
for (element of arrow.current) { element.bend(); }
However, as said before, element does not have a bend element by default and you have not appended at any point. Only arrow has a bend property.
I hope this guides you on why this does not work.
However, if you want to open a question on what you are trying to achieve, maybe we can help to get it fixed.
You need to call bend() on arrow object. In bend() function, you do what you need to do.
var Arrow = function() {
this.current = $('.arrow');
this.previous = null;
this.bend = function() {
// do bend
current.style = 'bent';
};
};
var arrow = new Arrow();
arrow.bend();
So two things.
You called the right method on the wrong object
arrow.bend(); // not arrow.current.bend()
The second possible problem is with this.current = $('.arrow');. To get the an element from the DOM, you should make sure it's totally loaded. I'd suggest the following
var Arrow = function($arrow) {
this.current = $arrow;
this.previous = null;
};
// To avoid creating the `bend` in every instance of Arrow
Arrow.prototype.bend = function() {
console.log(this.current.attr('id'));
};
$(function () {
// Now it's certain that the DOM is completely loaded
var arrow = new Arrow($('.arrow').first());
arrow.bend();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="arrow" id="toto">arrow<div>

how does $(this) work when nothing is selected

I just got into caching my jquery objects but not sure how to properly do it when using (this).
Also the only time I know how to use (this) is when its inside a click object or function so like:
$(".trigger").click(function () {
if ($(this).hasClass('toggle')) {
$(this).closest("td").next().find(".clueless > div").slideUp();
$(this).removeClass('toggle');
} else {
$(this).closest("td").next().find(".clueless > div").slideDown();
$(this).addClass('toggle');
}
});
so if I wanted to cache the $(this).closest("td").next().find(".clueless > div") the only thing I could think would be:
var $something = $(".trigger").find(this).closest("td").next().find(".clueless > div");
I will not completely re-iterate how the this keyword works, but there's an exhaustive explanation here.
In JS when the default this behaviour is not altered
Keeping things simple, to know the object to which the this keyword refers to you can simply look at the left-side of the . in a function invocation.
For example, in myObj.someFunction(), the this keyword within someFunction will point to myObj (that is unless the function has been bound using Function.prototype.bind).
If the function is not invoked on an object, such as someFunction(), then this will point to the global object which is window in browsers.
This is also the case within anonymous functions that are passed around, except for addEventListener, which will make sure that the this value within the handler is the object to which the handler was attached.
E.g.
setTimeout(function () { this; /*window*/ }, 10);
document.addEventListener('click', function (e) {
e.target; //the clicked DOM element
this; //the document
});
When this is altered by the API
Using Function.prototype.call or Function.prototype.apply, it is possible to specify the object to which this will point to during a function execution.
Some libraries (e.g. jQuery) are taking advantage of that feature to make this point to an object that is more intuitive, rather than the global object.
E.g.
$('#someEl').on('click', function (e) {
this; //the DOM element that was clicked (not the jQuery wrapper)
});
When this is altered in such way by the library, there is no other way than looking at the library's documentation to see what this will be.
We can read from jQuery event docs that:
In addition to the event object, the event handling function also has
access to the DOM element that the handler was bound to via the
keyword this.
Rewriting your function
Now, here's how you could re-write your function:
$(".trigger").click(function () {
var $this = $(this).toggleClass('toggle'),
$elementToSlide = $this.closest("td").next().find(".clueless > div"),
isToggled = !$this.hasClass('toggle'),
slideBehavior = isToggled? 'slideUp' : 'slideDown';
$elementToSlide[slideBehavior]();
});
Not sure exactly what you're trying to achieve but you can cache $(this) whenever it has context.
$(".trigger").click(function () {
var $trigger = $(this);
if ($trigger.hasClass('toggle')) {
$trigger.closest("td").next().find(".clueless > div").slideUp();
$trigger.removeClass('toggle');
} else {
$trigger.closest("td").next().find(".clueless > div").slideDown();
$trigger.addClass('toggle');
}
});
If you want to cache $(this).closest("td").next().find(".clueless > div") then
$(".trigger").click(function () {
var el = $(this).closest("td").next().find(".clueless > div");
if ($(this).hasClass('toggle')) {
el.slideUp();
$(this).removeClass('toggle');
} else {
el.slideDown();
$(this).addClass('toggle');
}
});
I assume this is what you are going for, it would make sense to cache $(this) since you are using it multiple times
var $this = $(this);
would do it

Manipulate object's property while in event handler

I've learned that for scope reasons the this keyword inside an event listener, which is embedded in an object, doesn't refer to the global object but rather to the element which triggered the event.
Now, I understand that if I want to fetch a property I can save it to a variable before the event handler is called. But what can I do if I want to manipulate the property's value?
In the following piece of code I am trying to manipulate the drugCount property within the removeDrug event listener.
var Drugs = {
drugs: $("#drugs_table"),
drugRow: $("#drug").html(),
drugCount: 0,
init: function() {
this.addDrugRow();
this.removeDrugRowHandler();
},
addDrugRow: function() {
this.drugCount++;
this.drugs.append(this.drugRow.replace(/{{id}}/,this.drugCount));
$(".drugsSelect").select2();
},
removeDrugRowHandler: function() {
drugCount = this.drugCount;
// also a problematic solution, because it only retains the inital drugCount.
// i.e I need a way to access the "live" count from within the event
$(document).on("click",".removeDrug",function(){
if (drugCount>0) {
$(this).parents("tr").remove();
this.drugCount--; // how should I approach this?
}
});
}
}
Try This
var Drugs = function() {
var me = this;
me.drugs = $("#drugs_table");
me.drugRow = $("#drug").html();
me.drugCount = 0;
me.init = function() {
this.addDrugRow();
this.removeDrugRowHandler();
};
me.addDrugRow = function() {
this.drugCount++;
this.drugs.append(this.drugRow.replace(/{{id}}/,this.drugCount));
$(".drugsSelect").select2();
};
me.removeDrugRowHandler= function() {
var drugCount = me.drugCount;
$(document).on("click",".removeDrug",function(){
if (drugCount>0) {
$(this).parents("tr").remove();
me.drugCount--;
}
});
}
}
As it turns out the easy solution is to use the object name instead of the contextual this.
So instead of this.drugCount I used Drugs.drugCount.
However, this solution only works if I am in the context of a single object. If I were to write a "class" (i.e var Drugs = function(){ ... }) this won't work.

JavaScript's Array call and functions fail inside Prototype

I have this code:
function Keyboard() {
this.log = $('#log')[0];
this.pressedKeys = [];
this.bindUpKeys = function() {
$('body').keydown(function(evt) {
this.pressedKeys.push(evt.keyCode);
var li = this.pressedKeys[evt.keyCode];
if (!li) {
li = this.log.appendChild(document.createElement('li'));
this.pressedKeys[evt.keyCode] = li;
}
$(li).text('Down: ' + evt.keyCode);
$(li).removeClass('key-up');
});
}
this.bindDownKeys = function() {
$('body').keyup(function(evt) {
this.pressedKeys.push(evt.keyCode);
var li = this.pressedKeys[evt.keyCode];
if (!li) {
li = this.log.appendChild(document.createElement('li'));
}
$(li).text('Up: ' + evt.keyCode);
$(li).addClass('key-up');
});
}
}
I get these errors:
TypeError: 'undefined' is not an object (evaluating 'this.pressedKeys.push')
It doesn't matter what I want to do with the Array, it just keeps giving me access errors, as if it doesn't exists inside the prototype.
What am I doing wrong? :( I'm just accessing the array as any other value inside the prototype). Are there problems with objects inside objects?
The problem is that inside the event handler this is not what you think. You can bind the event handler function with the bind method (or, since it looks like you're using jQuery, $.proxy):
this.bindUpKeys = function() {
var that = this;
$('body').keydown($.proxy(function(evt) {
//Use `this` as normal inside here
}, this));
}
Or you can store a reference to this outside of the event handler e.g.
this.bindUpKeys = function() {
var that = this;
$('body').keydown(function(evt) {
//Use `that` instead of `this` in here
});
}
as if it doesn't exists inside the prototype.
No, it does.
I'm just accessing the array
That's what you don't. this does not point to your Keyboard instance inside an event listener.
When the function is called as an event listener the DOM element will be the context (jQuery does that, too). See MDN's overview for the this keyword. You can use a closure-scoped variable to hold a reference to the actual instance, as for example described here (there are many questions about that).
Possible quick-fixes:
$('body').keydown( (function(){...}).bind(this))
var that=this; $('body').keydown(function(){ that.pressedKeys...; });

Redefining a jQuery function

I need to redefine the jQuery val() function, but I don't want to mess with the source code. Is it possible to modify it on just one element?
Specifically, I need to do something like this:$("div.smth").val = function() {return this.innerText;};. However, the val function is not modified by this code. What am I doing wrong?
You should instead modify the function of the prototype (jQuery calls this fn). This is where all functions like $(...).something inherit from.
$.fn.val = function() { ... };
If you want to save the original function:
var old = $.fn.val; // `old` won't be overwritten
$.fn.val = function() { ... };
This will do what you want, you need to attach your new val method to jQuery's plugin stack:
$.fn.val = function(value) {
return this[0].innerText;
}
The other answers indicate how to replace the .val() method, but if you know you only need this for one specific element can't you just do this:
$("div.smth")[0].innerText
But in any case isn't that pretty much what the existing jQuery .text() method does?
jsval: function(fn) {
var that = this;
var newfn = function(event) { fn.apply(that, arguments); };
this.click(newfn);
return newfn;
}
Instead now you can call your normal val and on that specific div, call jsval

Categories