myFunction is not defined - javascript

in my scriptfile.js I layout a simple text re-sizing function. On its own, the function works as expected. However, when I try to add $(window).resize(fitheadliner()); to my scriptfile to fire the the fitheadliner() function on the resize event, it get a "Uncaught ReferenceError: fitheadliner is not defined" error. I have moved the resize function around the scriptfile thinking it may be a scope issue to no avail. Here is the contents of the file:
( function( $ ) {
$.fn.fitheadliner = function() {
this.each(function() {
var
$headline = $(this),
$parent = $headline.parent();
var
textW = $headline.width(),
parentW = $parent.width(),
ratio = parentW / textW;
var
originalSize = parseFloat($headline.css('font-size')),
newSize = originalSize * (0.9 * ratio);
$headline.css("font-size", newSize);
});
};
$(window).resize(fitheadliner());
} )( jQuery );

Here:
$.fn.fitheadliner = …
fitheadliner is defined as a property of $.fn.
But here:
$(window).resize(fitheadliner());
you are attempting to access it as a variable (and call it). Consider:
(function($) {
function fitheadliner() {
...
}
// Assign a reference, don't call the function
$(window).resize(fitheadliner);
}(jQuery));
However, you have a further issue from:
this.each(function() {
The function is called with window as this, and window doesn't have an each method. I don't understand how you aren't seeing other errors (or erroneous behaviour). Perhaps this should be replaced with a selector:
$(<selector>).each(...);

It's not quite a scoping issue. More like a qualification issue.
When this line executes
$(window).resize(fitheadliner());
You are saying, run $(window), then run fitheadliner(), then run the .resize method of the return value of the first call, passing it the return value of the first function call.
It's easy to think that the manner in which you are calling fitheadliner() would tie it to the $ object, but it doesn't. There's no reason it would. Each expression is evaluated independently and then chained appropriately.
Therefore, this expression is looking for a symbol in scope named fitheadliner that must be of type function. There are no symbols in scope with that name. There is, however, a symbol named $.fn.fitheadliner
To get past this error, you need to fully-qualify the reference, like
$(window).resize($.fn.fitheadliner());
But the fact is, I don't think that is totally what you want either. .resize takes a handler function. fitheadliner does not return a function, or return anything. It actually does some work. So I think what you meant to do was to pass a reference to fitheadliner to resize.
That's easy - take the paranthesis out.
$(window).resize($.fn.fitheadliner);
Now, even better, there is probably no reason to attach fitheadliner to the jQuery prototype like that. Try this. It may more closer to what you were trying to do.
( function( $ ) {
function fitheadliner() {
this.each(function() {
var
$parent = $(this).parent(),
$headline = $(this);
var
textW = $(this).width(),
parentW = $parent.width(),
ratio = parentW / textW;
var
originalSize = parseFloat($(this).css('font-size')),
newSize = originalSize * (0.9 * ratio);
$(this).css("font-size", newSize);
});
}
$(window).resize(fitheadliner);
} )( jQuery );
This defines a function in scope called fitheadliner and then passes a reference to it to resize.
Personally, I would take it one step further and inline the function anonymously since it does not need to be reused. But it's a matter of form/preference for me. There's semantically no difference.
( function( $ ) {
$(window).resize(function fitheadliner() {
this.each(function() {
var
$parent = $(this).parent(),
$headline = $(this);
var
textW = $(this).width(),
parentW = $parent.width(),
ratio = parentW / textW;
var
originalSize = parseFloat($(this).css('font-size')),
newSize = originalSize * (0.9 * ratio);
$(this).css("font-size", newSize);
});
});
} )( jQuery );

Related

Accessing variables from another function is showing reference error?

I have two functions. The first function calculates variables on domcontentloaded and resize. The second function triggers on both domcontentloaded and scroll. I need 3 variables from the first function inside the second function to calculate some stuff. I am trying to get the return variable array from 1st function upme() to use inside second function doso() - I am getting this error poss isn't defined at htmldocument.doso
JAVASCRIPT
document.addEventListener('DOMContentLoaded', upme);
window.addEventListener('resize', upme);
function upme()
{
var rome = document.getElementById("out-cmnt");
var rect = rome.getBoundingClientRect();
// console.log(rect.top, rect.right, rect.bottom, rect.left);
var poss = rect.top + window.scrollY; var iwwr = window.innerWidth;
var koss = rect.bottom + window.scrollY; var loss = koss - poss;
return [poss, loss];
}
document.addEventListener('DOMContentLoaded', doso);
window.addEventListener('scroll', doso);
function doso()
{
lopp = document.getElementById("Web_1920__1");
hope = lopp.clientHeight; const meme = document.body.scrollHeight;
const keke = hope/meme; const scsc = window.scrollY;
var innr = window.innerHeight;
var saka = upme(); // problem here calling 1st function
var noss = poss - innr + loss; // problem here
if(scsc > noss && window.matchMedia("(min-width: 765px)").matches)
{
// doing something
}
}
doso();
How can I successfully get those variables like poss, loss, koss inside the second function doso() - ? Please help me out. Thanks to everyone involved in this community.
When calling a function, you can only get values you chose to return.
If you want to use them by name you need to declare them with the wanted names. In your example, you only have an array called saka with [0] being poss and [1] being loss.
First, here you can only access poss and loss because that's the only two variables you are returning in the upme function.
You return them as an array, there is an easy way in javascript to retrieve and name variables returned in an array:
var [poss, loss] = upme();
With this snippet of code, you say that the array you are returning from this function is of size 2 and that you want to declare 1 variable for each element, by naming them respectively poss and loss.
If you need more variables, just return more of them in upme then declare and name them when calling the function.
You could also create an object, but this solution is good enough for your problem.
Change this line:
var noss = poss - innr + loss;
to:
var noss = saka.poss - innr + saka.loss;

naturalWidth and naturalHeight returns 0 using onload event

I have read countless of answers of this issue and I came up with the following, but it doesn't work either.
function fitToParent(objsParent, tagName) {
var parent, imgs, imgsCant, a, loadImg;
//Select images
parent = document.getElementById(objsParent);
imgs = parent.getElementsByTagName(tagName);
imgsCant = imgs.length;
function scaleImgs(a) {
"use strict";
var w, h, ratioI, wP, hP, ratioP, imgsParent;
//Get image dimensions
w = imgs[a].naturalWidth;
h = imgs[a].naturalHeight;
ratioI = w / h;
//Get parent dimensions
imgsParent = imgs[a].parentNode;
wP = imgsParent.clientWidth;
hP = imgsParent.clientHeight;
ratioP = wP / hP;
//I left this as a test, all this returns 0 and false, and they shouldn't be
console.log(w);
console.log(h);
console.log(ratioI);
console.log(imgs[a].complete);
if (ratioP > ratioI) {
imgs[a].style.width = "100%";
} else {
imgs[a].style.height = "100%";
}
}
//Loop through images and resize them
var imgCache = [];
for (a = 0; a < imgsCant; a += 1) {
imgCache[a] = new Image();
imgCache[a].onload = function () {
scaleImgs(a);
//Another test, this returns empty, for some reason the function fires before aplying a src to imgCache
console.log(imgCache[a].src);
}(a);
imgCache[a].src = imgs[a].getAttribute('src');
}
}
fitToParent("noticias", "img");
To summarise, the problem is the event onload triggers before the images are loaded (or that is how I understand it).
Another things to add:
I don't know at first the dimensions of the parent nor the child,
because they varied depending of their position on the page.
I don't want to use jQuery.
I tried with another function, changing the onload event to
window, and it worked, but it takes a lot of time to resize because
it waits for everything to load, making the page appear slower,
that's how I came to the conclusion the problem has something to do
with the onload event.
EDIT:
I made a fiddle, easier to look at the problem this way
https://jsfiddle.net/whn5cycf/
for some reason the function fires before aplying a src to imgCache
Well, the reason is that you are calling the function immedeatly:
imgCache[a].onload = function () {
}(a);
// ^^^ calls the function
You call the function and assign undefined (the return value of that function) to .onload.
If you want to use an IIFE to capture the current value of a, you have to make it return a function and accept a parameter to which the current value of a is assigned to:
imgCache[a].onload = function (a) {
return function() {
scaleImgs(a);
};
}(a);
Have a look again at JavaScript closure inside loops – simple practical example .

create javascript function to toggle sprite

I am working on creating a JS/jQuery function to toggle the position of an icon sprite. So far I have gotten this code to work:
$('.toggle-completed').mouseup(function(){
var sp = ($(this).css('background-position'));
$(this).css('background-position',
sp = sp == '-82px 50%'? '-101px 50%' : '-82px 50%'
);
});
However I want to abstract this so that it can work with any sprite pair. Something like this:
var toggleSprite = function(firstPosition, secondPosition){
var sp = ($(this).css('background-position'));
$(this).css('background-position',
sp = sp == firstPosition ? secondPosition : firstPosition
);
};
Which would be called as follows:
toggleSprite('-82px 50%', '-101px 50%');
However this code generates an error:
Uncaught TypeError: Cannot use 'in' operator to search for
'backgroundPosition' in undefined
I am thinking that either 1. I can't really use 'this' in a function. Or perhaps I am not calling the function correctly.
Thank you.
The problem seems to be in the is the this reference... it does not looks like to be referencing the target element.
I think you can write it as a simple plugin like
$.fn.toggleSprite = function (firstPosition, secondPosition) {
return $(this).css('background-position', function(idx, sp){
return sp = sp == firstPosition ? secondPosition : firstPosition;
});
};
then call it like
$(this).toggleSprite('pos1', 'pos2');//or $(el)
or pass the elements as a param to the method
var toggleSprite = function (els, firstPosition, secondPosition) {
$(els).each(function () {
var sp = $(this).css('background-position');
$(this).css('background-position', sp = sp == firstPosition ? secondPosition : firstPosition);
});
};
then
toggleSprite(myels, 'pos1', 'pos2')
A third option is to pass a custom context to the toggle method using Function.call()
toggleSprite('-82px 50%', '-101px 50%').call(myel);
Inside of a jQuery callback, this often refers to the DOM element that triggered the event, so can be used in a selector ($(this)), but in your example that creates an error, the context is a vanilla js function, so this refers to the current scope, not a DOM object.
Perhaps extend the jQuery fn object...
jQuery.fn.toggleSprite = function(firstPosition, secondPosition){
var sp = ($(this).css('background-position'));
$(this).css('background-position',
sp = sp == firstPosition ? secondPosition : firstPosition
);
// also, return $(this) so that chaining is not broken...
return $(this);
};
And call like this...
$('.some.selector').toggleSprite('-82px 50%', '-101px 50%');
So that the function knows what this refers to.
The problem you're having has to do with the fact that the function context this is not bound to a specific DOM element when used in a generic (i.e. non-jQuery) function. One of the more powerful features of the jQuery library is that the function context this is bound to the DOM element that calls the jQuery function, allowing the user to easily target a specific element. In regular JavaScript, this refers to the current scope, which in your case is the function toggleSprite.
For your purposes, I think a simple jQuery plugin function would do the trick:
$.fn.toggleSprite = function(pos1, pos2) {
return $(this).css('background-position', function(_, sp){
return sp == pos1 ? pos2 : pos1;
});
};
The benefit of this method is that you can then call this function on any jQuery object as you normally would:
$('.sprite').toggleSprite('-82px 50%', '-101px 50%');

Doubts About Use of Practical Closure

I'm trying to find out more about closures in Javascript and was going through this: https://developer.mozilla.org/en/JavaScript/Guide/Closures#Practical_closures
According to this article, by using such a function:
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
We can then make use of such statements to increase/decrease the font-size of text on a page:
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
While I understand the concept here - i.e. size12, size14 and size16 become closures that allow access to the internal function, I can't help but feel that this is unnecessary. Isn't it easier to just have:
function makeSizer(size) {
document.body.style.fontSize = size + 'px';
}
, and then invoke it with these?
document.getElementById('size-12').onclick = makeSizer(12);
document.getElementById('size-14').onclick = makeSizer(14);
document.getElementById('size-16').onclick = makeSizer(16);
Can anyone tell me if my thinking is right - or maybe I'm just a novice to Javascript and doesn't understand the advantage to using closure in this scenario, in which case I'll be most glad if you can explain the advantage of doing so.
Thanks in advance guys.
No, you can't do that.
It's as if you had written:
document.getElementById('size-12').onclick = (function(size) {
document.body.style.fontSize = size + 'px';
})(12);
The function gets immediately invoked, the style will be applied straight away, and no .onclick handler gets registered because the return value of the function is undefined.
The real point of the example is to show that you can return a function from another function, and that you can then assign that result to an event handler.
If you had left makeSizer() unmodified then you could assign the handlers as proposed without intermediate variables, i.e.:
document.getElementById('size-12').onclick = makeSizer(12);
but that won't work if you change makeSizer() the way you described.
It is also less efficient than storing the "sizer" in a variable if you use the same sizer more than once.
For the example you presented, of course closure is not necessary, but I guess it is just to make it simple to present the concept. There are cases though that closure is the best solution to use: think about how to implement a "private" attribute in javascript or when you need curryng to encapsulate arguments (ie, for a callback function).
I hope the following example helps:
var makeSequencer = function() {
var _count = 0; // not accessible outside this function
var sequencer = function () {
return _count++;
}
return sequencer;
}
var fnext = makeSequencer();
var v0 = fnext(); // v0 = 0;
var v1 = fnext(); // v1 = 1;
var vz = fnext._count // vz = undefined
Yes, those variables (sizeN) are unnecessary. You can directly assign the result of makeSizer() as handlers, which looks far better.
But, the use of these variables is not the concept of closures. The closure in this example is the function makeSizer, which returns a function (even without arguments), which still has access to the size variable.
Though, you need to see the difference between
function makeSizer(size) {
return function resize() {
document.body.style.fontSize = size + 'px';
};
}
and
function resize(size) {
document.body.style.fontSize = size + 'px';
}
Executing makeSizer(5) does not do anything, it returns a function that sets the size to the pre-defined size when invoked. Instead executing resize(5) does set the size directly. You can't use the result of the latter function as an event handler.

call function inside a nested jquery plugin

There are many topics related to my question and i have been through most of them, but i haven't got it right. The closest post to my question is the following:
How to call functions that are nested inside a JQuery Plugin?
Below is the jquery plugin i am using. On resize, the element sizes are recalculated. I am now trying to call the function resizeBind() from outside of the jquery plugin and it gives me error
I tried the following combinations to call the function
$.fn.splitter().resizeBind()
$.fn.splitter.resizeBind()
Any ideas, where i am getting wrong?
;(function($){
$.fn.splitter = function(args){
//Other functions ......
$(window).bind("resize", function(){
resizeBind();
});
function resizeBind(){
var top = splitter.offset().top;
var wh = $(window).height();
var ww = $(window).width();
var sh = 0; // scrollbar height
if (ww <0 && !jQuery.browser.msie )
sh = 17;
var footer = parseInt($("#footer").css("height")) || 26;
splitter.css("height", wh-top-footer-sh+"px");
$("#tabsRight").css("height", splitter.height()-30+"px");
$(".contentTabs").css("height", splitter.height()-70+"px");
}
return this.each(function() {
});
};
})(jQuery);
I had the same problem. Those answers on related posts didn't work for my case either. I solved it in a round about way using events.
The example below demonstrates calling a function that multiplies three internal data values by a given multiplier, and returns the result. To call the function, you trigger an event. The handler in turn triggers another event that contains the result. You need to set up a listener for the result event.
Here's the plugin - mostly standard jQuery plugin architecture created by an online wizard:
(function($){
$.foo = function(el, options){
// To avoid scope issues, use 'base' instead of 'this'
var base = this;
// Access to jQuery and DOM versions of element
base.$el = $(el);
base.el = el;
// Add a reverse reference to the DOM object
base.$el.data("foo", base);
base.init = function(){
base.options = $.extend({},$.foo.defaultOptions, options);
// create private data and copy in the options hash
base.private_obj = {};
base.private_obj.value1 = (base.options.opt1);
base.private_obj.value2 = (base.options.opt2);
base.private_obj.value3 = (base.options.opt3);
// make a little element to dump the results into
var ui_element = $('<p>').attr("id","my_paragraph").html(base.private_obj.value1 +" "+ base.private_obj.value2+" " +base.private_obj.value3);
base.$el.append(ui_element);
// this is the handler for the 'get_multiplied_data_please' event.
base.$el.bind('get_multiplied_data_please', function(e,mult) {
bar = {};
bar.v1 = base.private_obj.value1 *mult;
bar.v2 = base.private_obj.value2 *mult;
bar.v3 = base.private_obj.value3 *mult;
base.$el.trigger("here_is_the_multiplied_data", bar);
});
};
base.init();
}
$.foo.defaultOptions = {
opt1: 150,
opt2: 30,
opt3: 100
};
$.fn.foo = function(options){
return this.each(function(){
(new $.foo(this, options));
});
};
})(jQuery);
So, you can attach the object to an element as usual when the document is ready. And at the same time set up a handler for the result event.
$(document).ready(function(){
$('body').foo();
$('body').live('here_is_the_multiplied_data', function(e, data){
console.log("val1:" +data.v1);
console.log("val2:" +data.v2);
console.log("val3:" +data.v3);
$("#my_paragraph").html(data.v1 +" "+ data.v2+" " +data.v3);
});
})
All that's left is to trigger the event and pass it a multiplier value
You could type this into the console - or trigger it from a button that picks out the multiplier from another UI element
$('body').trigger('get_multiplied_data_please', 7);
Disclaimer ;) - I'm quite new to jQuery - sorry if this is using a hammer to crack a nut.
resizeBind function is defined as private so you cannot access it from outside of it's scope. If you want to use it in other scopes you need to define it like that
$.fn.resizeBind = function() { ... }
Then you would call it like that $(selector').resizeBind()
You have defined the resizeBind function in a scope that is different from the global scope. If you dont'use another javascript framework or anything else that uses the $ function (to prevent conflict) you can delete the
(function($){
...
})(jQuery);
statement and in this way the function will be callable everywhere without errors
I didn't test it:
this.resizeBind = function() { .... }

Categories

Resources