function scope in jQuery - javascript

I have this code in <script> tags at the top of my HTML file.
$(document).ready(function()
{
$('#scrollbar1').tinyscrollbar();
$('a.jqtree_common').click(updateScrollbar());
});
$(function updateScrollbar()
{
var oScrollbar = $('#scrollbar1');
oScrollbar.tinyscrollbar();
oScrollbar.tinyscrollbar_update();
$('a.jqtree_common').click(updateScrollbar());
});
But for some reason when I run this, it says updateScrollbar() is undefined within (document).ready. When I try to define updateScrollBar() inside of (document).ready then updateScrollBar() gets caught in some kind of endless loop.
My question is twofold:
What can I do to make updateScrollBar() defined within the scope of (document).ready?
Is there a better way to assign this function to the 'a.jqtree_common' elements? They are created dynamically at runtime, and modified as the webpage is used. I want the function to run every time one of them is clicked.
I'm using tiny scrollbar and jqtree
EDIT: I want the $('a.jqtree_common').click(updateScrollbar); assignment to be made every time the scrollbar is updated, since I believe clicking on a 'a.jqtree_common' element creates more 'a.jqtree_common' elements.

Pass the function reference as the callback, instead of the result of the function in Click event. () will invoke the function and set the result as a call back which inturn calls updatescrollbar inside it again and goes in an infinite loop.
$(document).ready(function()
{
$('#scrollbar1').tinyscrollbar();
$('a.jqtree_common').click(updateScrollbar);
});
function updateScrollbar()
{
var oScrollbar = $('#scrollbar1');
oScrollbar.tinyscrollbar();
oScrollbar.tinyscrollbar_update();
//$('a.jqtree_common').click(updateScrollbar);
}

Okay, I know there are a lot of hats in the ring at this point, but here's my entry just the same...
//IMMEDIATELY-INVOKED FUNCTION EXPRESSION (IIFE)
// Used for privacy/variable scoping
(function(){
//bind init function to dom-ready event
$(init); //same as $(document).ready(init);
//initialize event bindings for page
function init() {
//initialize scrollbar
$('#scrollbar1').tinyscrollbar();
//click binding for tree
$('a.jqtree_common').click(updateScrollbar);
//assuming you want to run the updateScrollbar on page load
//in addition to clicks
updateScrollbar();
}
//handles scrollbar updates
function updateScrollbar() {
//assuming the tinyscrollbar() initialization only needs
//to happen once, inside the initialization event.
$('#scrollbar1').tinyscrollbar_update();
}
}());
The structure above is pretty much how I work through things... I do my variables first, then event bindings at the top, and have my function declarations below. This works because of function hoisting (in compilation of the JS, function declarations are moved to the top), this doesn't work with function assignments (ex: var x = function(){...}), then I wrap the whole thing inside an IIFE. I find that this structure provides easier readability and comprehension. I don't like putting my bindings at the bottom, as I find you have to go over a lot to get to what you are looking for.

Try this code:
$(document).ready(function() {
var updateScrollbar = function () {
var oScrollbar = $('#scrollbar1');
oScrollbar.tinyscrollbar();
oScrollbar.tinyscrollbar_update();
};
$('#scrollbar1').tinyscrollbar();
$('a.jqtree_common').click(updateScrollbar).click();
});

To bind elements loaded later we must use this .on( function
$(document).ready(function(){
$('#scrollbar1').tinyscrollbar();
$(document).on("click","a.jqtree_common", updateScrollbar);
});
function updateScrollbar(){
var oScrollbar = $('#scrollbar1');
oScrollbar.tinyscrollbar();
oScrollbar.tinyscrollbar_update();
}
and if you want the scope limitation then declare function within the block
$(document).ready(function(){
$('#scrollbar1').tinyscrollbar();
$(document).on("click","a.jqtree_common", updateScrollbar);
function updateScrollbar(){
var oScrollbar = $('#scrollbar1');
oScrollbar.tinyscrollbar();
oScrollbar.tinyscrollbar_update();
}
});

This is what ultimately worked for me:
$(document).ready(function()
{
$('#scrollbar1').tinyscrollbar();
$('a.jqtree_common').click(updateScrollbar);
function updateScrollbar()
{
var oScrollbar = $('#scrollbar1');
oScrollbar.tinyscrollbar();
oScrollbar.tinyscrollbar_update();
$('a.jqtree_common').click(updateScrollbar);
}
});

Related

How to trigger element.on('mouseleave') in a Uint Test to call my AngularJS function

I want to call myFunction. Then I want to verify it was called with a Unit Test.
Javascript:
var self = this;
self.myFunction= function myFunction() {
// do stuff
};
self.onClickChange = function onClickChange() {
$('.checkBox').on('mouseleave', function(){
self.myFunction();
});
};
I have tried this test:
it('should call myFunction', inject(function ($compile, $rootScope) {
spyOn(this.instance, 'onClickChange').and.callThrough();
spyOn(this.instance, 'myFunction').and.callThrough();
this.instance.onClickChange('messageBoard');
var scope = $rootScope.$new(),
element = $compile('<md-checkbox class="checkBox"></md-checkbox>')(scope);
element.triggerHandler('mouseleave');
scope.$digest();
expect(this.instance.myFunction).toHaveBeenCalled();
}));
I have also tried using
scope.$broadcast('mouseleave');
And
var event = document.createEvent('MouseEvents');
event.initEvent('mouseleave', true, true);
$('.checkBox').dispatchEvent(event);
And this instead of the last line in the previous three lines:
document.body.dispatchEvent(event);
How can I get .on('mouseleave') to work and ferify that myFunction has been called?
Several things here. First, to answer your immediate question, you are calling: this.instance.onClickChange('messageBoard'); before you have created any DOM. So, you are going to try adding the click handler to absolutely nothing.
You need to add the click handler only after you create the DOM when you call compile.
But, there's another problem. You are compiling the directive and creating a DOM element, but it is not rooted in the document, so using the jquery $ on the document will not work.
It is bad style to use jQuery selectors in angular.
You could do something like this, where you use the $element of the current scope.
var self = this;
var $element = getElement(); // do some magic to get the current element
self.myFunction= function myFunction() {
// do stuff
};
self.onClickChange = function onClickChange() {
$element.on('mouseleave', function(){
self.myFunction();
});
};
And then your tests could work in the same way. The difference is that the $element never needs to be added to the DOM in order for the code to work.
But really, I would avoid this whole approach. You should not be using jQuery inside of angular. That is an outdated style. Instead, I recommend using ng-click to add the handler directly to the DOM.

Function is being called without trigger event

I have a function that I want to use in order to expand menus in various places. I expect it to be triggered on a click of menu associated button, but at the moment it is being called on page load (I assume from within document ready), and class 'expanded' is added without clicking on a buton. I am confused to why this happens, as it should be called .on('click' ..
jQuery(document).ready(function ($) {
'use strict';
$('#btn-expand-mobile-nav').on('click', showMenu('#side-navigation'));
});
function showMenu(menu) {
var x = $(menu),
y = 'expanded';
if (x.hasClass(y))
x.removeClass(y);
else
x.addClass(y);
}
You are calling the function immediately. Instead defer the execution using an anonymous function:
$('#btn-expand-mobile-nav').on('click', function(){
showMenu('#side-navigation')
});
If there were no parameters you could have done this:
$('#btn-expand-mobile-nav').on('click', showMenu);
Simplistic explanation for #skobaljic:
By using the function name alone, you are pointing at the variable showMenu and saying call this later as a function.
By using function(){} you are saying, here is a temp variable, containing a function, that you can call later.
e.g. it is the same as:
var temp = function(){
showMenu('#side-navigation')
}
$('#btn-expand-mobile-nav').on('click', temp); // No parenthesis on temp
As #Dave Newton rightly points out, this can be simplified using toggleClass:
$('#btn-expand-mobile-nav').on('click', function(){
$('#side-navigation').toggleClass("expanded");
});

Function not working creating a variable and using jquery?

I have a function which changes the width of some images. This happens when I hover over a different div in a different function. For some reason the the called function only performs some lines of code and then it stops.
function hey()
{
alert(0);
var $imgContent = ('.imgContent');
$imgContent.css("width","10%");
alert(2);
}
var $content = $('.content');
$content.mouseenter(function() {
$content.removeClass('full').addClass('partial');
$(this).addClass('full').removeClass('partial');
$(this).find('.img1').css('display','none');
$(this).find('.img2').css('display','');
if($(this).hasClass('cont1')){
alert(1);
hey();
}
if($(this).hasClass('cont2')){
}
if($(this).hasClass('cont3')){
}
if($(this).hasClass('cont4')){
}
}).mouseleave(function(){
$(this).find('.img1').css('display','');
$(this).find('.img2').css('display','none');
$(this).removeClass('full').addClass('partial');
});
In the mouseenter() function when I check if $(this).hasClass('cont1') then I perform an alert, which works. After that I call on function hey(). This is where my problem arises. After calling function hey() i perform another alert(0) , which also works. But the lines of code after that do not get executed and the last alert(2) doesn't work either.
There is an error in your code.
Replace:
var $imgContent = ('.imgContent');
With
var $imgContent = $('.imgContent');
You have a mistake in your jquery object definition.
Per jQuery():
jQuery() — which can also be written as $() — searches through the DOM for any elements that match the provided selector and creates a new jQuery object that references these elements
so in your case you should have:
var $imgContent = $('.imgContent');
$imgContent.css("width","10%");
Also, it's important to note that in your .mouseenter() function you refer to:
$content.removeClass('full').addClass('partial');
$(this).addClass('full').removeClass('partial');
$content and $(this) both refer to the same object, so in essence these lines are pointless.

Anonymous function on page load

I'm trying to get better with JavaScript and learn how to utilize my code in functions and keep everything clean. I'm trying to run a function on page-load...
var setColors = function(){
this.init = function(){
$.getJSON('js/colors.json', function(colors) {
$.each(colors, function(i, colors) {
$('<li>', {
text: colors['color'],
'name' : colors['color'],
'data-hex' : colors['hex'],
'data-var' : colors['var']
}).appendTo('#picker');
})
});
}
}
(This is not a color-picker, just a list of colors)
I want setColors() to be executed as soon as the page starts. I read that an anonymous function runs automatically, but this one isn't, I also tried...
$(function(){
setColors();
});
Below the setColors() function and that isn't working ether (The page is just blank). What am I doing wrong and how do I get my function to run on page load? I'm trying to learn so an explanation would be great.
Anonymous functions are not run immediately, you're thinking of Immediately Invoked Function Expressions which happen to often use an anonymous function.
To fix your code:
a) get rid of the this.init function wrapper within the "object" - you're not using it and this.foo only makes sense if you're using new to instantiate an object:
function setColors() {
return $.getJSON(...);
}
Note that returning the $.getJSON() result allows you to register additional deferred object handlers, register error handlers, etc.
b) call the above function in a document.ready handler (which you must do, since the AJAX callback modifies the DOM).
$(setColors);
NB: the latter is a legal way of calling this handler - jQuery will automatically register any function that you pass this way as a document.ready handler. It's similar to writing:
$(function() { setColors() })
but without the extra (useless) function wrapper.
To have that run once the DOM is initialized, you can put it in a ready listener (jQuery):
$(document).on('ready', function() {
setColors();
});
If you want the function to run automatically as soon as it is encountered in the js, after the } that ends the function, add ();
Something like:
function setColors() {
// Code
}();
setColors doesn't return the next function, or call it at the end. YOu could change it to look like:
var setColors = function(){
this.init = function(){
$.getJSON('js/colors.json', function(colors) {
$.each(colors, function(i, colors) {
$('<li>', {
text: colors['color'],
'name' : colors['color'],
'data-hex' : colors['hex'],
'data-var' : colors['var']
}).appendTo('#picker');
})
});
}
init(); // <--- change
}
Which would do the trick for you. You don't even need to "return it" either since the init function itself doesn't return anything, so you could just call it.

JavaScript doesn't see a variable outside function

I have this function:
$(function ($) {
...
});
var getNotifyBar = $(".NotifyBar");
function showNotify(text) {
getNotifyBar.hide().find(".text").html(text).end().slideDown();
}
And when I use function showNotify(text) nothing happens. But when I put it in the JavaScript console (of the browser) it works.
More than likely this is running before all the elements with class NotifyBar are rendered
var getNotifyBar = $(".NotifyBar");
Which means that it is empty when you try to use it later. you should do this instead:
var getNotifyBar;
$(function ($) {
getNotifyBar = $(".NotifyBar");
});
Now it should be properly loaded. Next, you need to remember that getNotifyBar is a reference to a jQuery object already loaded from a selector. As such, you do not need to wrap it in $(). You should make this change:
function showNotify(text) {
getNotifyBar.hide().find(".text").html(text).end().slideDown();
}
Your code has no chance of working now and works from console, because when you run it in console it's after the DOMReady. Put the code in place of your 3 dots in the example and it will work, because:
$(function($){
//code here
})
will run the code after DOMReady

Categories

Resources