Window load inside self invoking jQuery plugin function - javascript

I have the following jQuery plugin code to do something on window load or scroll:
Note: I simplified the code for the example.
;(function ($, window, document, undefined) {
'use strict';
$.scrollFn = function (el, options) {
var base = this;
// jQuery and DOM of element
base.$el = $(el);
base.el = el;
// Cached
base.$win = $(window);
base.$doc = $(document);
// Initialize
base.init = function () {
base.options = $.extend({}, $.scrollFn.defaultOptions, options);
};
// Scroll handler
base.scrollHandler = function () {
console.log('scrolled or loaded');
console.log(base.options.exampleOption);
};
// On scroll and load
base.$win.on('scroll load', base.scrollHandler);
};
$.scrollFn.defaultOptions = {
exampleOption: "Test"
};
$.fn.scrollFn = function (options) {
return this.each(function () {
(new $.scrollFn(this, options));
});
};
})(jQuery, window, document);
And in another JS I am initializing the scroll function:
$(document).ready(function() {
$('.example').scrollFn({
exampleOption: "Hello world"
});
});
The issue: It is picking up window scroll but not load. When the window loads I do not see the console return scrolled or loaded. It shows only when I scroll.
How can I make load work as well?
I tried (inside scrollFn):
$(window).on('scroll load', base.scrollHandler);
base.$doc.on('scroll load', base.scrollHandler);
$(document).ready(function() {
base.$win.on('scroll load', base.scrollHandler);
});
They do not work.

I had to simply add base.scrollHandler in the init function. I will except any other answer providing more info or a better solution if possible.

Related

Test Case to call function on Document.ready

My JS code is mentioned below:
if (typeof IPL === "undefined") {
IPL = {};
}
/// <summary>Declare Namespace IPL.Register</summary>
if (typeof IPL.Register === "undefined") {
IPL.Register = {};
}
$(document).ready(function () {
IPL.Register.Print.initialize();
});
/// <summary>Declare Namespace IPL.Register.Print</summary>
IPL.Register.Print =
{
/// <summary>Function to call on initialize page.</summary>
initialize: function () {
window.onload = function () {
window.print();
};
}
};
When I run Test case (Qunit.js and blanket.js) as mentioned below then Document.ready function is not getting called and Code coverage not cover that line.
Below test case works fine but it only include last lines of code and initialize function on window load.
test("initialize test", 1, function () {
var result = IPL.Register.Print.initialize();
equal(undefined, result, "passed");
});
Someone please assist how to write Test case to execute function on document load?
The best way to do this is by not testing the load event at all. Just put the event handler into a named function and then test the behaviour of that function.
/// <summary>Declare Namespace IPL.Register.Print</summary>
IPL.Register.Print = {
/// <summary>Function to call on initialize page.</summary>
initialize: function() {
window.onload = function() {
window.print();
};
},
print: function() {
window.print();
}
};
test("initialize test", 1, function() {
var result = IPL.Register.Print.print();
equal(undefined, result, "passed");
});
http://jsfiddle.net/Tintin37/vpqjsr8L/
The load event just runs your function, what do you want specifically achieve by testing the load event ?

Magento Jquery Bootstrap and Prototype conflict? nav-tabs dont work

I use Magento 1.9.2.4 and the following Theme
https://github.com/webcomm/magento-boilerplate
The description of it is "HTML5 Twitter Bootstrap 3.1 Magento Boilerplate Template"
It works fine with everything else of "Bootstrap responsive".
My problem is, that all of the following on this Site did not work on my
Installation:
http://www.w3schools.com/bootstrap/bootstrap_tabs_pills.asp
And "did not work" means you can click on next tab but the border and highlighting and so on is still on the first tab.
Same thing on "nav-pills" of Bootstrap.
In this Boilerplate there is a Workaround for Bootstrap dropdown:
jQuery.noConflict();
;(function ($) {
'use strict';
function Site(settings) {
this.windowLoaded = false;
}
Site.prototype = {
constructor: Site,
start: function () {
var me = this;
$(window).load(function () {
me.windowLoaded = true;
});
this.attach();
},
attach: function () {
this.attachBootstrapPrototypeCompatibility();
this.attachMedia();
},
attachBootstrapPrototypeCompatibility: function () {
/*// Bootstrap and Prototype don't play nice, in the sense that
// prototype is a really wacky horrible library. It'll
// hard-code CSS to hide an element when a hide() event
// is fired. See http://stackoverflow.com/q/19139063
// To overcome this with dropdowns that are both
// toggle style and hover style, we'll add a CSS
// class which has "display: block !important"
$('*').on('show.bs.dropdown show.bs.collapse active nav-tabs.active', function (e) {
$(e.target).addClass('bs-prototype-override');
});
$('*').on('hidden.bs.collapse nav-tabs', function (e) {
$(e.target).removeClass('bs-prototype-override');
});*/
var isBootstrapEvent = false;
if (window.jQuery) {
var all = jQuery('*');
jQuery.each(['hide.bs.dropdown',
'hide.bs.collapse',
'hide.bs.modal',
'hide.bs.tooltip',
'hide.bs.popover',
'hide.bs.tab'], function (index, eventName) {
all.on(eventName, function (event) {
isBootstrapEvent = true;
});
});
}
var originalHide = Element.hide;
Element.addMethods({
hide: function (element) {
if (isBootstrapEvent) {
isBootstrapEvent = false;
return element;
}
return originalHide(element);
}
});
},
attachMedia: function () {
var $links = $('[data-toggle="media"]');
if (!$links.length) return;
// When somebody clicks on a link, slide the
// carousel to the slide which matches the
// image index and show the modal
$links.on('click', function (e) {
e.preventDefault();
var $link = $(this),
$modal = $($link.attr('href')),
$carousel = $modal.find('.carousel'),
index = parseInt($link.data('index'));
$carousel.carousel(index);
$modal.modal('show');
return false;
});
}
};
jQuery(document).ready(function ($) {
var site = new Site();
site.start();
});
})(jQuery);
I already asked on github with no response question on github
So the Dropdown of Bootstrap is working.
My Question am i doing anything wrong or am i missing something?
Why does the nav-tabs not work in here?
Adding the "'bower_components/bootstrap/js/tab.js'," tab.js to the gulpfile.js AND
adding the tab class to the script.js solved my Problem with Bootstrap Nav Pills and Tabs.
$('*').on('show.bs.dropdown show.bs. hide.bs.tab hidden.bs.tab', function(e) {
$(e.target).addClass('bs-prototype-override');
});
$('*').on('hidden.bs.collapse', function(e) {
$(e.target).removeClass('bs-prototype-override');
});
At the end the code looks just like this:
jQuery.noConflict();
;(function($) {
'use strict';
function Site(settings) {
this.windowLoaded = false;
}
Site.prototype = {
constructor: Site,
start: function() {
var me = this;
$(window).load(function() {
me.windowLoaded = true;
});
this.attach();
},
attach: function() {
this.attachBootstrapPrototypeCompatibility();
this.attachMedia();
},
attachBootstrapPrototypeCompatibility: function() {
// Bootstrap and Prototype don't play nice, in the sense that
// prototype is a really wacky horrible library. It'll
// hard-code CSS to hide an element when a hide() event
// is fired. See http://stackoverflow.com/q/19139063
// To overcome this with dropdowns that are both
// toggle style and hover style, we'll add a CSS
// class which has "display: block !important"
$('*').on('show.bs.dropdown show.bs. hide.bs.tab hidden.bs.tab', function(e) {
$(e.target).addClass('bs-prototype-override');
});
$('*').on('hidden.bs.collapse', function(e) {
$(e.target).removeClass('bs-prototype-override');
});
},
attachMedia: function() {
var $links = $('[data-toggle="media"]');
if ( ! $links.length) return;
// When somebody clicks on a link, slide the
// carousel to the slide which matches the
// image index and show the modal
$links.on('click', function(e) {
e.preventDefault();
var $link = $(this),
$modal = $($link.attr('href')),
$carousel = $modal.find('.carousel'),
index = parseInt($link.data('index'));
$carousel.carousel(index);
$modal.modal('show');
return false;
});
}
};
jQuery(document).ready(function($) {
var site = new Site();
site.start();
});
})(jQuery);

Jquery plugin callback on element selector

I'm creating a plugin that works with scrolling to detect when an element is visible in the viewport. I'm trying to add a callback option that executes anything that's passed in it when an element is targeted. Below are the main parts of my plugin.
$.myPlugin = function(el, options) {
var base = this;
// Access to jQuery and DOM versions of element
base.$el = $(el);
base.el = el;
// Initialize
base.init = function() {
base.options = $.extend({}, $.myPlugin.defaultOptions, options);
};
// when element is visible in the viewport
if (base.inviewport()) {
// Callback
if (typeof base.options.callback === 'function') {
base.options.callback.call(this);
}
return;
}
// Run initializer
base.init();
$.fn.myPlugin = function(options) {
return this.each(function() {
(new $.myPlugin(this, options));
});
};
}
Now I'm trying to use the callback:
$(function() {
$('.some-class').myPlugin({
callback: function() {
console.log($(this));
}
});
});
When I scroll to the element selector I'm expecting the callback to return the element with the class name some-class but instead it returns window
[Window, jquery: "1.10.2", constructor: function, init: function, selector: "", toArray: function…]
Please inform what I'm doing wrong...

AngularJS : How to run JavaScript from inside Directive after directive is compiled and linked

I have a responsive template that I am trying to use with my Angularjs app. This is also my first Angular app so I know I have many mistakes and re-factoring in my future.
I have read enough about angular that I know DOM manipulations are suppose to go inside a directive.
I have a javascript object responsible for template re-sizes the side menu and basically the outer shell of the template. I moved all of this code into a directive and named it responsive-theme.
First I added all the methods that are being used and then I defined the App object at the bottom. I removed the function bodies to shorten the code.
Basically the object at the bottom is a helper object to use with all the methods.
var directive = angular.module('bac.directive-manager');
directive.directive('responsiveTheme', function() {
return {
restrict: "A",
link: function($scope, element, attrs) {
// IE mode
var isRTL = false;
var isIE8 = false;
var isIE9 = false;
var isIE10 = false;
var sidebarWidth = 225;
var sidebarCollapsedWidth = 35;
var responsiveHandlers = [];
// theme layout color set
var layoutColorCodes = {
};
// last popep popover
var lastPopedPopover;
var handleInit = function() {
};
var handleDesktopTabletContents = function () {
};
var handleSidebarState = function () {
};
var runResponsiveHandlers = function () {
};
var handleResponsive = function () {
};
var handleResponsiveOnInit = function () {
};
var handleResponsiveOnResize = function () {
};
var handleSidebarAndContentHeight = function () {
};
var handleSidebarMenu = function () {
};
var _calculateFixedSidebarViewportHeight = function () {
};
var handleFixedSidebar = function () {
};
var handleFixedSidebarHoverable = function () {
};
var handleSidebarToggler = function () {
};
var handleHorizontalMenu = function () {
};
var handleGoTop = function () {
};
var handlePortletTools = function () {
};
var handleUniform = function () {
};
var handleAccordions = function () {
};
var handleTabs = function () {
};
var handleScrollers = function () {
};
var handleTooltips = function () {
};
var handleDropdowns = function () {
};
var handleModal = function () {
};
var handlePopovers = function () {
};
var handleChoosenSelect = function () {
};
var handleFancybox = function () {
};
var handleTheme = function () {
};
var handleFixInputPlaceholderForIE = function () {
};
var handleFullScreenMode = function() {
};
$scope.App = {
//main function to initiate template pages
init: function () {
//IMPORTANT!!!: Do not modify the core handlers call order.
//core handlers
handleInit();
handleResponsiveOnResize(); // set and handle responsive
handleUniform();
handleScrollers(); // handles slim scrolling contents
handleResponsiveOnInit(); // handler responsive elements on page load
//layout handlers
handleFixedSidebar(); // handles fixed sidebar menu
handleFixedSidebarHoverable(); // handles fixed sidebar on hover effect
handleSidebarMenu(); // handles main menu
handleHorizontalMenu(); // handles horizontal menu
handleSidebarToggler(); // handles sidebar hide/show
handleFixInputPlaceholderForIE(); // fixes/enables html5 placeholder attribute for IE9, IE8
handleGoTop(); //handles scroll to top functionality in the footer
handleTheme(); // handles style customer tool
//ui component handlers
handlePortletTools(); // handles portlet action bar functionality(refresh, configure, toggle, remove)
handleDropdowns(); // handle dropdowns
handleTabs(); // handle tabs
handleTooltips(); // handle bootstrap tooltips
handlePopovers(); // handles bootstrap popovers
handleAccordions(); //handles accordions
handleChoosenSelect(); // handles bootstrap chosen dropdowns
handleModal();
$scope.App.addResponsiveHandler(handleChoosenSelect); // reinitiate chosen dropdown on main content resize. disable this line if you don't really use chosen dropdowns.
handleFullScreenMode(); // handles full screen
},
fixContentHeight: function () {
handleSidebarAndContentHeight();
},
setLastPopedPopover: function (el) {
lastPopedPopover = el;
},
addResponsiveHandler: function (func) {
responsiveHandlers.push(func);
},
// useful function to make equal height for contacts stand side by side
setEqualHeight: function (els) {
var tallestEl = 0;
els = jQuery(els);
els.each(function () {
var currentHeight = $(this).height();
if (currentHeight > tallestEl) {
tallestColumn = currentHeight;
}
});
els.height(tallestEl);
},
// wrapper function to scroll to an element
scrollTo: function (el, offeset) {
pos = el ? el.offset().top : 0;
jQuery('html,body').animate({
scrollTop: pos + (offeset ? offeset : 0)
}, 'slow');
},
scrollTop: function () {
App.scrollTo();
},
// wrapper function to block element(indicate loading)
blockUI: function (ele, centerY) {
var el = jQuery(ele);
el.block({
message: '<img src="./assets/img/ajax-loading.gif" align="">',
centerY: centerY !== undefined ? centerY : true,
css: {
top: '10%',
border: 'none',
padding: '2px',
backgroundColor: 'none'
},
overlayCSS: {
backgroundColor: '#000',
opacity: 0.05,
cursor: 'wait'
}
});
},
// wrapper function to un-block element(finish loading)
unblockUI: function (el) {
jQuery(el).unblock({
onUnblock: function () {
jQuery(el).removeAttr("style");
}
});
},
// initializes uniform elements
initUniform: function (els) {
if (els) {
jQuery(els).each(function () {
if ($(this).parents(".checker").size() === 0) {
$(this).show();
$(this).uniform();
}
});
} else {
handleUniform();
}
},
updateUniform : function(els) {
$.uniform.update(els);
},
// initializes choosen dropdowns
initChosenSelect: function (els) {
$(els).chosen({
allow_single_deselect: true
});
},
initFancybox: function () {
handleFancybox();
},
getActualVal: function (ele) {
var el = jQuery(ele);
if (el.val() === el.attr("placeholder")) {
return "";
}
return el.val();
},
getURLParameter: function (paramName) {
var searchString = window.location.search.substring(1),
i, val, params = searchString.split("&");
for (i = 0; i < params.length; i++) {
val = params[i].split("=");
if (val[0] == paramName) {
return unescape(val[1]);
}
}
return null;
},
// check for device touch support
isTouchDevice: function () {
try {
document.createEvent("TouchEvent");
return true;
} catch (e) {
return false;
}
},
isIE8: function () {
return isIE8;
},
isRTL: function () {
return isRTL;
},
getLayoutColorCode: function (name) {
if (layoutColorCodes[name]) {
return layoutColorCodes[name];
} else {
return '';
}
}
};
}
};
});
Originally the App.init() object method would be called at the bottom of any regular html page, and I have others that do certain things also that would be used on specific pages like Login.init() for the login page and so forth.
I did read that stackoverflow post
"Thinking in AngularJS" if I have a jQuery background? and realize that I am trying to go backwards in a sense, but I want to use this template that I have so I need to retro fit this solution.
I am trying to use this directive on my body tag.
<body ui-view="dashboard-shell" responsive-theme>
<div class="page-container">
<div class="page-sidebar nav-collapse collapse" ng-controller="SidemenuController">
<sidemenu></sidemenu>
</div>
<div class="page-content" ui-view="dashboard">
</div>
</div>
</body>
So here is my problem. This kinda sorta works. I don't get any console errors but when I try to use my side menu which the javascript for it is in the directive it doesn't work until I go inside the console and type App.init(). After that all of the template javascript works. I want to know how to do responsive theme stuff in these directives. I have tried using it both in the compile and link sections. I have tried putting the code in compile and link and calling the $scope.App.init() from a controller and also at the bottom after defining everything. I also tried putting this in jsfiddle but can't show a true example without having the console to call App.init().
My end design would be having some way to switch the pages through ui-router and when a route gets switched it calls the appropriate methods or re-runs the directive or something. The only method that will run on every page is the App.init() method and everything else is really page specific. And technically since this is a single page app the App.init() only needs to run once for the application. I have it tied to a parent template inside ui-router and the pages that will switch all use this shell template. There are some objects that need to access other to call their methods.
Im sorry in advance for maybe a confusing post. I am struggling right now trying to put together some of the ways that you do things from an angular perspective. I will continue to edit the post as I get responses to give further examples.
You said I have read enough about angular that I know DOM manipulations are suppose to go inside a directive but it sounds like you missed the point of a directive. A directive should handle DOM manipulation, yes, but not one directive for the entire page. Each element (or segment) of the page should have its own directive (assuming DOM manip needs to be done on that element) and then the $controller should handle the interactions between those elements and your data (or model).
You've created one gigantic directive and are trying to have it do way too much. Thankfully, you've kinda sorta designed your code in such a way that it shouldn't be too hard to break it up into several directives. Basically, each of your handle functions should be its own directive.
So you'd have something like:
.directive('sidebarMenu', function(){
return {
template: 'path/to/sidebar/partial.html',
link: function(scope, elem, attrs){
// insert the code for your 'handleSidebarMenu()' function here
}
};
})
.directive('horizontalMenu', function(){
return {
template: 'path/to/horizontal/partial.html',
link: function(scope, elem, attrs){
// insert the code for your 'handleHorizontalMenu()' function here
}
};
})
and then your view would look something like:
<body ui-view="dashboard-shell" responsive-theme>
<div class="page-container">
<div class="page-sidebar nav-collapse collapse">
<horizontal-menu></horizontal-menu>
<sidebar-menu></sidebar-menu>
</div>
<div class="page-content" ui-view="dashboard">
</div>
</div>
</body>
And then you don't need a SidebarmenuController because your controller functions shouldn't be handling DOM elements like the sidebar. The controller should just handling the data that you're going to display in your view, and then the view (or .html file) will handle the displaying and manipulation of that data by its use of the directives you've written.
Does that make sense? Just try breaking that huge directive up into many smaller directives that handle specific elements or specific tasks in the DOM.

Call the same instance of jQuery plugin

I have written a jQuery plugin below and would like to be able to call it again for the same instance on an element.
The plugin goes...
(function($) {
$.fn.myPlugin = function(options){
var settings = {
color: null
};
if (options) {
$.extend(settings, options);
}
return this.each(function(){
var self = this;
var pics = $('li', self);
function refresh() {
pics = $('li', self);
};
$('a', self).click(function(){
pics.filter(':last').remove();
alert(settings.color);
refresh();
return false;
});
});
}
})(jQuery);
In the page this is called...
$('#test').myPlugin({ color: 'blue' });
Now I want to call the same plugin for the same instance but pass the string refresh as the option whilst all the other variables are the same (so color would still be blue) e.g...
$('#test').myPlugin('refresh');
This would then execute the refresh() function.
How could I achieve that with the above?
Edit: To make it clearer I am thinking of how jQuery UI does their plugins. In the sortable plugin you can do $("#sortable").sortable(); and then $("#sortable").sortable('refresh'); on the same element. This is what I am trying to achieve.
You can store your instance with .data() and check for it when creating an instance.
Something like:
$.fn.doStuff = function () {
var ob = $(this);
var data = ob.data();
if (data.doStuff !== undefined) {
return data.doStuff;
}
doStuff;
});
(function($) {
$.fn.myPlugin = function(options){
var init = function($self, ops){
$self.find("a").click(function(){
pics.filter(':last').remove();
alert(settings.color);
refresh();
return false;
});
};
this.refresh = function(){
//your code here
};
return this.each(function(){
var self = this;
var pics = $('li', self);
var settings = {
color: null
};
var ops = $.extend(true, settings, options);
init($(this), ops);
});
}
})(jQuery);
try something like this. and you can call refresh() like $().myPlugin().refresh();

Categories

Resources