Variable in nested function returns undefined - javascript

The variable my_sound is declared in the first, outer function. So, I should be able to use it in the nested function. However the mouseout event produces no result. What am I doing wrong? Thanks for any help.
$(document).ready(function () {
var starting_pics = ["CN.gif", "EN.gif", "GN.gif"];
var starting_sounds = ["CN.mp3", "EN.mp3", "GN.mp3"];
var i = 0;
for (i = 0; i < starting_pics.length; i++) {
$("<img/>").attr("src", "images/" + starting_pics[i]).appendTo("#main").addClass("pics");
}
$("#main").on("click", ".pics", function () {
var i = $(this).index();
var my_sound =($("<audio/>").attr("src", "audio/" + starting_sounds[i])).load().get(0).play();
$("#main").on("mouseout", ".pics", function () {
$("my_sound").animate({ volume: 0 }, 1000);
});
});
});

The problem is probably that .play() doesn't return a jQuery object (or anything, for that matter, hence undefined).
Additionally, as the other comments have said, you don't want $('my_sound').whatever but rather just my_sound.whatever if it were a jQuery object, which it is not. So maybe you could try
var $my_sound = $("<audio />").attr("suchandsuch","etc");
$my_sound.load().get(0).play();
$my_sound.whatever();

Related

Need help in my jquery plugin

Last week a made a function for ellipsing the text inside some selector.
I was calling the function like this:
ellipsiText('.class',50) passing the selector and the max length of the text that i wanted to. This works fine, but im trying to make it a plugin, to call like this: $('.class').ellipsiText(50).
So, i was reading the tutorial in jquery website, and i understood how to do it. But i think i'm having an issue with the "this" seletor. Here is my original function:
function ellipsiText(selector,maxLength){
var array = $(selector).map(function(){
return $(this).text();
}).get();
var i;
var teste = [];
for (i=0;i<array.length;i++){
if (array[i].length > maxLength){
teste.push(array[i].substr(0,maxLength) + "...");
} else {
teste.push(array[i]);
}
}
for (var i=0;i<teste.length;i++){
$(selector).each(function(i){
$(this).text(teste[i]);
});
}
}
and here is my tentative of making a jquery plugin:
(function ($) {
$.fn.ellipsiText = function(length){
var array = $(this).map(function(){
return $(this).text();
}).get();
var i;
var teste = [];
for (i = 0; i < array.length; i++){
if (array[i] > length){
teste.push(array[i].substr(0,length) + "...");
} else {
teste.push(array[i]);
}
}
$(this).each(function(i){
$(this).text(teste[i]);
});
};
}(jQuery));
What am i doing wrong?
Well first thing is not a problem, but instead of $(this) in the first function scope, you can use this.map/this.each.
The problem is, in the second code you do
if (array[i] > length)
instead of
if (array[i].length > length)
Nothing to do with the jQuery plugin!
http://jsfiddle.net/UY88r/
This is untested, but the basic structure is something like this. Also you have so much looping in your code when one loop is needed.
$.fn.ellipsiText= function(options) {
var settings = $.extend({ //nice way to give to give defaults
length : 50,
ellipsi : "..."
}, options );
return this.each(function() { //the return is needed for chaining
var elem = $(this);
var txt = elem.text();
if (txt.length>settings.length) {
elem.text(txt.substr(0,settings.length) + settings.ellipsi );
}
});
};
and call it
$( "div.defaults" ).ellipsiText();
$( "div.foo" ).ellipsiText({
length : 10
});
$( "div.more" ).ellipsiText({
length : 10,
ellipsi : "<more>"
});
You already have a working function, just use it.
$.ellipsiText = ellipsiText;
$.fn.ellipsiText = function (count) {
ellipsiText(this, count);
}
now you can use it like any of the following:
ellipsiText('.class',50);
$.ellipsiText('.class',50);
$('.class').ellipsiText(50);
There's no sense in rewriting the function you already have working when you can just use it.

Defining javascript function while referencing local variable

Is it possible to use the current value of a variable when defining a javascript function?
In the following code I'd like to add a click function to an array of divs, each with a different value for i, ie. the first div should call setCurrentStyleIndex(0). At the moment they all call whatever the value of i is at the time of the call.
Sorry if this is dumb question.
Step3.populateStyleMenu = function() {
var stylePopup = $("#stylePopup");
for(var i = 0; i < Step3.fontStyles.length; i++) {
var div = $('<div>'+Step3.fontStyles[i][0]+'</div>');
div.css({
'font-weight': Step3.fontStyles[i][1],
'font-style': Step3.fontStyles[i][2],
})
div.data('style', Step3.fontStyles[i]);
div.addClass('personalizePopupItem personalizeStyleItem');
div.click(function() {
setCurrentStyleIndex(i);
});
stylePopup.append(div);
}
}
try to create a closure (not tested):
(function (i) {
div.click(function() {
setCurrentStyleIndex(i);
});
})(i);
Yup, you can do the below:
div.click((function() {
var inx = i;
return function() {
setCurrentStyleIndex(inx);
}
})());

Javascript reference in loop: "Uncaught TypeError: Cannot read property 'value' of undefined"

I tried debugging my code for like a few hour but I got nothing out of it. The issue is that it makes absolutely no sense on why it reports an error every time I tried to use document.forms[0][i] (i as the iterator) in the event listener but "this" satisfies the code.
//broken
var addListeners = function() {
var i;
var formFields = document.forms[0];
var formSubmit = formFields["submit"];
for (i = 0; i < formFields.length; i++) {
if (formFields[i] != formSubmit) {
formFields[i].onblur = (function () {
checkNonEmpty(formFields[i]);
});
}
}
};
//works
var addListeners = function() {
var i;
var formFields = document.forms[0];
var formSubmit = formFields["submit"];
for (i = 0; i < formFields.length; i++) {
if (formFields[i] != formSubmit) {
formFields[i].onblur = (function () {
checkNonEmpty(this);
});
}
}
};
Wouldn't "this" refer to document.forms[0][i]?... formFields references to document.forms[0]. However the exact same code (with "this" where formFields[i] is at) works just fine.
Here is the demo: http://jsfiddle.net/PbHwy/
Cranio's answer already contains the root of the matter. To get rid of this you can either include formFields[i] by using closures
var blurCallbackGenerator = function(element){
return function () {
checkNonEmpty(element);
};
};
formFields[i].onblur = blurCallbackGenerator(formFields[i]);
/* // dense version:
formFields[i].onblur = (function(element){
return function () {
checkNonEmpty(element);
};
})(formFields[i]);
*/
or simply using this.
See also:
MDN: Creating closures in loops: A common mistake
Because you define formFields in a scope outside (or better, different than) the event listener. When the event listener is called, it is called not in the addListeners function where you define formFields, but "independently", so the reference is lost and its value is undefined (but this works because it is not dependent on that scope).
The problem is that the variable i (referred to in each of your handlers) is the exact same variable in each of them, which by the time the loop has finished has value formFields.length+1 and is therefore wrong for all of them. Try this instead [note: the below used to say something VERY WRONG before I edited it -- thanks to Zeta for pointing out my mistake]:
var addListeners = function() {
var i;
var formFields = document.forms[0];
var formSubmit = formFields["submit"];
for (i = 0; i < formFields.length; i++) {
if (formFields[i] != formSubmit) {
formFields[i].onblur = (function(j) {
return (function () {
checkNonEmpty(formFields[j]);
})(i);
});
}
}
};
and you'll find it works (unless there's another bug that I haven't noticed).
If you can afford to support only Javascript 1.7 and above, you can instead write your old code but make your for look like this: for (let i=0; i<formFields.length; i++). But you quite possibly can't.

Prototype: observing 2 elements and pass different parameters?

CI have this code:
for(var i = 0; i < toObserve.length; i++) {
var elems = toObserve[i].split('###');
var elementToObserve = elems[0];
var imageToUse = elems[1];
$(elementToObserve).observe('click', respondToClick);
}
function respondToClick(event) {
var element = event.element();
}
In the respondToClick function I need a different image (imageToUse) for each elementToObserve. How can I do that? Can I pass a param or something?
Thanks!
Addition: I tried what Diodeus suggested, but it seems that only the last passed parameter is used, when any of the elements I observe is clicked. Whats wrong or is the way I want to do it not the right one?
Use an anonymous function:
$(elementToObserve).observe('click', function(event) {
var yourVar = "moo";
respondToClick(event,yourVar)
});
Use the specialised bindAsEventListener.
$(elementToObserve).observe('click',
respondToClick.bindAsEventListener(imageToUse)
);
The bound arguments will then be passed after the event parameter.
function respondToClick(event, imageToUse)
{
var element = event.element();
element.src = imageToUse;
}
I got this to work by simply assigning my variable to the element object:
elementToObserve.imageToUse = imageToUse;
Then in the observer function:
function respondToClick(event) {
var imageToUse = event.target.imageToUse;
}
I haven't realized any drawbacks to this.

jQuery plugin development and the contexts

I'm trying to develop a jQuery plugin. Here's the code snippet I've written so far:
var MYNAMESPACE = MYNAMESPACE || {};
MYNAMESPACE.MyPlugin = function (inputElement, options) {
var element = $(inputElement),
oldValue = element.val(),
container = null,
init = function () {
container = $('<div class="container"></div>').hide().insertAfter(element);
//the suggest method is called from within the anonymous function
//called by getJSON...
},
suggest = function (resp) {
for (var i = 0; i < len; i++) {
var item = $('<div/>')
.html('whatever')
.mouseover(function (index) {
return function () {
activateItem(index);
};
} (i));
container.append(item);
}
},
activateItem = function (index) {
//container doesn't include those appended items.
//why???
};
init();
};
(function ($) {
$.fn.myplugin = function (options) {
return new MYNAMESPACE.MyPlugin(this.get(0), options);
};
} (jQuery));
Within the activateItem function, the container doesn't have any children!!! Why is that?
Any help would be highly appreciated,
container is already a defined jQuery object, but you are redefining to 'xxxx'. I'm guessing you want to insert the value? Try using container.html('xxxx')?
Also, it appears your mouseover function is returning a function where the index will be undefined (I haven't test it), but try this instead:
.mouseover(function () {
container.html('xxxx');
activateItem(i);
});
Update: Ok, now that I have time to look at the code, it appears len is not defined inside the suggest function. I made a rudimentary demo to test it all and it seems to work as expected.

Categories