Why is the js callback function giving an error - javascript

I have the following JS:
$(document).ready(function (e) {
$(".pStyle").CapFirstWord();
});
function CapFirstWord() {
$word = $(this);
var k = $word.length;
alert(k);
}
HTML:
<p class="pStyle">Of all of these "new" selectors, ::nth-letter is likely the most useful. For instance, Lettering.js wraps letters in for us so that we can select individual letters. This would be entirely unnecessary with ::nth-letter.</p>
I am trying to create a callback function which can be called from other pages to display the length of a given class/id.
How can I achieve that.

If you want to be able to chain your function after a jQuery selector you can extend the default functionality of jQuery using its $.fn.extend() method:
$.fn.extend({
CapFirstWord: function() {
$word = $(this);
var k = $word.length;
alert(k);
}
});
The jQuery.fn.extend() method extends the jQuery prototype ($.fn) object to provide new methods that can be chained to the jQuery() function.
Then you can call $(".pStyle").CapFirstWord();.
Demo
$.fn.extend({
CapFirstWord: function() {
$word = $(this);
var k = $word.length;
alert(k);
}
});
$(document).ready(function (e) {
$(".pStyle").CapFirstWord();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<p class="pStyle">Of all of these "new" selectors, ::nth-letter is likely the most useful. For instance, Lettering.js wraps letters in for us so that we can select individual letters. This would be entirely unnecessary with ::nth-letter.</p>

You are calling the function wrong. You have attempted to chain a regular function to a jquery object.
$(document).ready(function (e) {
CapFirstWord($(".pStyle"));
});
Here I have added a parameter to the function, the element that is being passed by the above script:
function CapFirstWord($word) {
And finally, to get the length of the string, I am calling .text() on the element:
var k = $word.text().length;
Final solution:
$(document).ready(function (e) {
CapFirstWord($(".pStyle"));
});
function CapFirstWord($word) {
var k = $word.text().length;
alert(k);
}

jQuery object doesn't have a function called CapFirstWord, that's why it fails. You should call that function passing the jQuery object as argument. Something like this:
$(document).ready(function (e) {
CapFirstWord($(".pStyle"));
});
function CapFirstWord(tag) {
var k = tag.length;
alert(k);
}

Related

jQuery $(this) not working when inside a function

I have this simple function that copies some html, and places it in another div.
If I put the code for the function in the click event it works fine, but when I move it into a function (to be used in multiple places) it no longer works.
Do you know why this is?
If I console.log($(this)); in the function it returns the window element.
function addHTMLtoComponent () {
var wrapper = $(this).closest(".wrapper");
var component = $(wrapper).find(".component");
var componentCodeHolder = $(wrapper).find('.target');
$(componentCodeHolder).text(component.html())
//console.log($(this));
}
$(".js_show_html").click(function () {
addHTMLtoComponent();
});
codepen here - http://codepen.io/ashconnolly/pen/ebe7a5a45f2c5bbe58734411b03e180e
Should i be referencing $(this) in a different way?
Regarding other answers, i need to put the easiest one:
$(".js_show_html").click(addHTMLtoComponent);
since you called the function manually the function doesn't know the "this" context, therefore it reverted back to use the window object.
$(".js_show_html").click(function () {
addHTMLtoComponent();
});
// Change to this
$(".js_show_html").click(function () {
// the call function allows you to call the function with the specific context
addHTMLtoComponent.call(this);
});
Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call
this in the context of the click() event is the element clicked. In the context of the function addHTMLtoComponent this no longer is a reference to the element clicked.
Try passing the clicked object to the function to maintain the object reference.
function addHTMLtoComponent ($obj) {
var $wrapper = $obj.closest(".wrapper");
var $component = $wrapper.find(".component");
var $componentCodeHolder = $wrapper.find('.target');
$componentCodeHolder.text($component.html());
}
$(".js_show_html").click(function () {
addHTMLtoComponent($(this));
});
The special keyword this, when you call a function by itself, is the window object (which is what you observed). For the behavior you need, just add a parameter to the function that loads the appropriate context:
function addHTMLtoComponent(context) {
var wrapper = $(context).closest(".wrapper");
var component = $(wrapper).find(".component");
var componentCodeHolder = $(wrapper).find('.target');
$(componentCodeHolder).text(component.html())
//console.log($(context));
}
$(".js_show_html").click(function() {
addHTMLtoComponent(this);
});
One thing you could consider is that addHTMLtoComponent() could be made into a jQuery function itself:
$.fn.addHTMLtoComponent = function() {
return this.each(function() {
var wrapper = $(this).closest(".wrapper");
var component = $(wrapper).find(".component");
var componentCodeHolder = $(wrapper).find('.target');
componentCodeHolder.text(component.html())
});
}
Now you can call it like any other jQuery method:
$(".js_show_html").click(function () {
$(this).addHTMLtoComponent();
});
The value of this in a jQuery method will be the jQuery object itself, so you don't need to re-wrap it with $(). By convention (and when it makes sense), jQuery methods operate on all elements referred to by the root object, and they return that object for further chained operations. That's what the outer return this.each() construction does.
Inside the .each() callback, you've got a typical jQuery callback situation, with this being set successively to each member of the outer jQuery object.
You have to pass the element as parameter to this function.
eg:
<div onclick="addHTMLtoComponent ($(this))"></div>

prototype this selector

If I'm using the following function :
clusters.prototype.shop_iqns_selected_class = function() {
if(this.viewport_width < 980) {
$(this.iqns_class).each(function() {
$(this.iqn).on('click', function() {
if($(this).hasClass('selected')) {
$(this).removeClass('selected');
} else {
$(this).addClass('selected');
}
});
});
}
}
To add a property to the clusters function, I know that using this.viewport_width I'm referring to the parent function where I have this.viewport_width defined, but when I'm using the jQuery selector $(this), am I referring to the parent of the $.on() function ?
In JavaScript, this is defined entirely by how a function is called. jQuery's each function calls the iterator function you give it in a way that sets this to each element value, so within that iterator function, this no longer refers to what it referred to in the rest of that code.
This is easily fixed with a variable in the closure's context:
clusters.prototype.shop_iqns_selected_class = function() {
var self = this; // <=== The variable
if(this.viewport_width < 980) {
$(this.iqns_class).each(function() {
// Do this *once*, you don't want to call $() repeatedly
var $elm = $(this);
// v---- using `self` to refer to the instance
$(self.iqn).on('click', function() {
// v---- using $elm
if($elm.hasClass('selected')) {
$elm.removeClass('selected');
} else {
$elm.addClass('selected');
}
});
});
}
}
There I've continued to use this to refer to each DOM element, but you could accept the arguments to the iterator function so there's no ambiguity:
clusters.prototype.shop_iqns_selected_class = function() {
var self = this; // <=== The variable
if(this.viewport_width < 980) {
// Accepting the args -----------v -----v
$(this.iqns_class).each(function(index, elm) {
// Do this *once*, you don't want to call $() repeatedly
var $elm = $(elm);
// v---- using `self` to refer to the instance
$(self.iqn).on('click', function() {
// v---- using $elm
if($elm.hasClass('selected')) {
$elm.removeClass('selected');
} else {
$elm.addClass('selected');
}
});
});
}
}
More reading (posts in my blog about this in JavaScript):
Mythical methods
You must remember this
Don't use this all throughout the code. Methods like $.each give you another reference:
$(".foo").each(function(index, element){
/* 'element' is better to use than 'this'
from here on out. Don't overwrite it. */
});
Additionally, $.on provides the same via the event object:
$(".foo").on("click", function(event) {
/* 'event.target' is better to use than
'this' from here on out. */
});
When your nesting runs deep, there's far too much ambiguity to use this. Of course another method you'll find in active use is to create an alias of that, which is equal to this, directly inside a callback:
$(".foo").on("click", function(){
var that = this;
/* You can now refer to `that` as you nest,
but be sure not to write over that var. */
});
I prefer using the values provided by jQuery in the arguments, or the event object.

jQuery - why can't I bind events to elements in a loop?

Here is my code:
var b = $(slipStream.conf.mainVis).find('p#prev');
b.click(function() {
slipStream.slideLeft();
return false;
});
b = $(slipStream.conf.mainVis).find('p#next');
b.click(function() {
slipStream.slideRight();
return false;
});
b = $(slipStream.conf.controls).find('li img');
console.log(b);
for (var l in b) {
l.click(function() {
var visIndex = l.index();
console.log(visIndex);
});
};
The first two bindings go through, no problem. But I can't loop through a collection and bind something to each member? (the console is telling me that "l.click is not a function.") Is this a limitation of jQuery or is my code off? This seems like it would be the way to do it, though...
When you enumerate over a jQuery object, the values being enumerated are actual DOM nodes and not jQuery wrappers. Therefore, they don't have a click method but you can wrap them again to get all the usual functionality.
Of course this is not necessary because you can simply attach a wrapper directly from your initial jQuery instance:
$(slipStream.conf.controls).find('li img').click(function() {
var visIndex = $(this).index();
console.log(visIndex);
});
This is the classic "loop variables don't work properly in callbacks" bug.
Your variable l no longer has the originally supplied value by the time the callback is invoked - it has whatever final value was assigned in the last pass through the loop.
[FWIW, l isn't actually a jQuery object, so you have to wrap it - $(l) to use it with jQuery]
The usual fix to the loop bug is to create an additional closure that returns a function bound to the current value:
for (var l in b) { // NB: don't use `for ... in ...` on array-like objects!
var make_cb = function(n) {
return function() {
var visIndex = $(n).index();
console.log(visIndex);
}
}
$(l).click(make_cb(l));
};
Fortunately, you don't need a loop at all - you can have jQuery automatically add the callback to every element by itself:
b = $(slipStream.conf.controls).find('li img');
b.click(function() {
var visIndex = $(this).index();
console.log(visIndex);
});
Could it be that the problem is the forloop. .click is part of the jQuery, so you must be sure that it's called on element that is wrapper with jQuery.
$.each(b, function (index, element) {
$(element).click(function() {
});
};
With each() you can iterate through a set of jQuery objects:
$(slipStream.conf.controls).find('li img').each(function(){
$(this).click(function() {
var visIndex = $(this).index();
console.log(visIndex);
});
});
$(this) will match the currently indexed object from the collection.

Create a function like jQuery(document).ready

How can I do that?
It seems that you can have multiple jQuery's ready() functions, and they will all run when the DOM is loaded.
So how can I create my own ready()-like function? :)
function _addEvent(e, evt, handler){
if(evt == "ready")
evt = "DOMContentLoaded";
if(typeof handler !== 'function')return;
if (e.addEventListener)
e.addEventListener(evt, handler, false);
else if (e.attachEvent)
e.attachEvent("on" + evt, handler);
else
{
var oldHandler = e["on" + evt];
function newHandler(event){
handler.call(e, event);
if(typeof oldhandler === 'function')oldhandler.call(e, event);
}
}
}
var _events = ["ready", "click", "mousedown"]; //...
var _myLib = function(item){
function eventWorker(item, event){
this.add = function(handler){
_addEvent(item, event, handler);
};
}
for(var i=0;i<_events.length;i++)
this[_events[i]] = (new eventWorker(item, _events[i])).add;
};
var MyLib = function(item){
return new _myLib(item);
};
MyLib(document).ready(function(){alert("I am ready!");});
Test =>
http://jsfiddle.net/vgraN/
First, you need to identify what it is you need the function for - is it to respond to a particular browser event?
jQuery's $(document).ready(fn) uses an array internally to hold the functions to execute when the DOM has loaded. Adding a new ready(fn) call appends the function fn to the array. When the DOM has loaded (which is detected in various ways according to which browser the code is executing within), each function in turn in the array is executed. Any functions added using ready(fn) after the DOM has loaded are executed immediately.
In summary, you can use an array to store the functions to execute whenever it is that you need to execute them.
Take a look at domready, a standalone port of the ready(fn) function from jQuery to get some ideas about how to go about it.
It sounds like you want to make an array of functions and append new callbacks to it.
It's not easy to do cross browser.
If you assume the DOMContentLoaded event exists then you can just make
var domready = (function () {
var cbs = [];
document.addEventListener("DOMContentLoaded", function () {
cbs.forEach(function (f) {
f();
});
});
return function (cb) {
cbs.push(cb);
};
})();
You can use other fallbacks like window.onload and a hackish scroll check like jQuery does.
I'd recommend either using domready or reading the source.
Do you want to create a function which when passed a function will call that function at a particular time? (Also, it can be called multiple times.) If so this is how I would do it it. (Based on jQuery code.)
var funcQueue = (function () {
var funcList = [];
function runAll() {
var len = funcList.length,
index = 0;
for (; index < len; index++)
funcList[index].call(); // you can pass in a "this" parameter here.
}
function add(inFunc) {
funcList.push(inFunc);
}
})();
To use:
funcQueue.add(function () { alert("one"); });
funcQueue.add(function () { alert("two"); });
funcQueue.runAll(); // should alert twice.

Adding a javascript function to an already existing javascript object

I wrote a javascript function for Telerik RadTreeView as follows:
function CheckedNodeValues() {
var checkedNodes = $(this).get_checkedNodes();
for (var n in checkedNodes) {
checkedNodes[n].get_value();
}
}
I would like to be able to do myRadTreeView.CheckedNodeValues() as opposed to CheckedNodeValues(myRadTreeView);
Is there a way to add a function to a class in Javascript?
I hope what I'm asking is clear, ideally, I would like extend the methods available on a class.
You should extend the prototype of your object, like this:
myRadTreeView.prototype.CheckedNodeValues = function () {
// your code here
};
You can read more about the prototype concept here: http://www.javascriptkit.com/javatutors/proto.shtml or here: http://javascriptweblog.wordpress.com/2010/06/07/understanding-javascript-prototypes/
Something like this should do ..
Telerik.Web.UI.RadTreeView.prototype.CheckedNodeValues = function() {
var checkedNodes = $(this).get_checkedNodes();
for (var n in checkedNodes) {
checkedNodes[n].get_value();
}
}
Absolutely:
myRadTreeView.CheckedNodeValues = function () {
// do totally rad stuff here
};
To call it you would use:
myRadTreeView.CheckedNodeValues();

Categories

Resources