Event Listener? - javascript

I have an infinite scroll class:
(function(){
"use strict";
var InfiniteScroll = function() {
this.init();
};
var p = InfiniteScroll.prototype = mc.BaseClass.extend(gd.BaseClass);
p.BaseClass_init = p.init;
/*
* Public properties
*/
p.loading = false;
/*
* Public methods
*/
p.init = function() {
// Super
this.BaseClass_init();
// Init
this.ready();
};
p.ready = function() {
this._initInfiniteScroll();
};
p._initInfiniteScroll = function() {
$(window).scroll(function(){
if($(window).scrollTop() == $(document).height() - $(window).height()){
if(!p.loading)
{
p.loading = true;
//send a message up to say loading
}
}
});
}
mc.InfiniteScroll = InfiniteScroll;
}(window));
Now this is called from another class by:
this.infiniteScroll = new mc.InfiniteScroll();
In the other class I wish to listen for when the scroll is fired from where I have the comment:
//send a message up to say loading
I was just wondering how I could go about this, I'm familiar with event listeners in AS3, but could someone point me in the right direction for js?

You can implement a custom event handler in your class on which you add listener functions in other classes like in this post.
Which you would use in your _initInfiniteScroll function like:
//send a message up to say loading
this.fire({ type: "ContentLoadingEvent" });
Though, as suggested in the post, creating a seperate class for your InfiniteScroll events is better.

Related

How to hook on library function (Golden Layout) and call additional methods

I'm using a library called Golden Layout, it has a function called destroy which will close all the application window, on window close or refesh
I need to add additional method to the destroy function. I need to removeall the localstorage aswell.
How do i do it ? Please help
Below is the plugin code.
lm.LayoutManager = function( config, container ) {
....
destroy: function() {
if( this.isInitialised === false ) {
return;
}
this._onUnload();
$( window ).off( 'resize', this._resizeFunction );
$( window ).off( 'unload beforeunload', this._unloadFunction );
this.root.callDownwards( '_$destroy', [], true );
this.root.contentItems = [];
this.tabDropPlaceholder.remove();
this.dropTargetIndicator.destroy();
this.transitionIndicator.destroy();
this.eventHub.destroy();
this._dragSources.forEach( function( dragSource ) {
dragSource._dragListener.destroy();
dragSource._element = null;
dragSource._itemConfig = null;
dragSource._dragListener = null;
} );
this._dragSources = [];
},
I can access the destroy method in the component like this
this.layout = new GoldenLayout(this.config, this.layoutElement.nativeElement);
this.layout.destroy();`
My code
#HostListener('window:beforeunload', ['$event'])
beforeunloadHandler(event) {
var originalDestroy = this.layout.destroy;
this.layout.destroy = function() {
// Call the original
originalDestroy.apply(this, arguments);
localStorage.clear();
};
}
Looking at the documentation, GoldenLayout offers an itemDestroyed event you could hook to do your custom cleanup. The description is:
Fired whenever an item gets destroyed.
If for some reason you can't, the general answer is that you can easily wrap the function:
var originalDestroy = this.layout.destroy;
this.layout.destroy = function() {
// Call the original
originalDestroy.apply(this, arguments);
// Do your additional work here
};
You may be able to do this for all instances if necessary by modifying GoldenLayout.prototype:
var originalDestroy = GoldenLayout.prototype.destroy;
GoldenLayout.prototype.destroy = function() {
// Call the original
originalDestroy.apply(this, arguments);
// Do your additional work here
};
Example:
// Stand-in for golden laout
function GoldenLayout() {
}
GoldenLayout.prototype.destroy = function() {
console.log("Standard functionality");
};
// Your override:
var originalDestroy = GoldenLayout.prototype.destroy;
GoldenLayout.prototype.destroy = function() {
// Call the original
originalDestroy.apply(this, arguments);
// Do your additional work here
console.log("Custom functionality");
};
// Use
var layout = new GoldenLayout();
layout.destroy();
Hooking into golden layout is the intended purpose for the events.
As briefly touched on by #T.J. Crowder, there is the itemDestroyed event which is called when an item in the layout is destroyed.
You can just listen for this event like such:
this.layout.on('itemDestroyed', function() {
localStorage.clear();
})
However, this event is called every time anything is destroyed, and propagates down the tree, even just by closing a tab. This means that if you call destroy on the layout root, you will get an event for every RowOrColumn, Stack and Component
I would recommend to check the item passed into the event and ignore if not the main window (root item)
this.layout.on('itemDestroyed', function(item) {
if (item.type === "root") {
localStorage.clear();
}
})

Concatenate function

The idea behind this to animate section with mousewheel - keyboard and swipe on enter and on exit. Each section has different animation.
Everything is wrapp inside a global variable. Here is a bigger sample
var siteGlobal = (function(){
init();
var init = function(){
bindEvents();
}
// then i got my function to bind events
var bindEvents = function(){
$(document).on('mousewheel', mouseNav());
$(document).on('keyup', mouseNav());
}
// then i got my function here for capture the event
var mouseNav = function(){
// the code here for capturing direction or keyboard
// and then check next section
}
var nextSection = function(){
// Here we check if there is prev() or next() section
// if there is do the change on the section
}
var switchSection = function(nextsection){
// Get the current section and remove active class
// get the next section - add active class
// get the name of the function with data-name attribute
// trow the animation
var funcEnter = window['section'+ Name + 'Enter'];
}
// Let's pretend section is call Intro
var sectionIntroEnter = function(){
// animation code here
}
var sectionIntroExit = function(){
// animation code here
}
}();
So far so good until calling funcEnter() and nothing happen
I still stuck to call those function...and sorry guys i'm really not a javascript programmer , i'm on learning process and this way it make it easy for me to read so i would love continue using this way of "coding"...Do someone has a clue ? Thanks
Your concatenation is right but it'd be better if you didn't create global functions to do this. Instead, place them inside of your own object and access the functions through there.
var sectionFuncs = {
A: {
enter: function() {
console.log('Entering A');
},
exit: function() {
console.log('Exiting A');
}
},
B: {
enter: function() {
console.log('Entering B');
},
exit: function() {
console.log('Exiting B');
}
}
};
function onClick() {
var section = this.getAttribute('data-section');
var functions = sectionFuncs[section];
functions.enter();
console.log('In between...');
functions.exit();
}
var buttons = document.querySelectorAll('button');
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', onClick);
}
<button data-section="A">A</button>
<button data-section="B">B</button>
You could have an object that holds these functions, keyed by the name:
var enterExitFns = {
intro: {
enter: function () {
// animation code for intro enter
},
exit: function () {
// animation code for intro exit
}
},
details: {
enter: function () {
// animation code for details enter
},
exit: function () {
// animation code for details exit
}
}
};
var name = activeSection.attr('data-name');
enterExitFns[name].enter();

jQuery plugin instances variable with event handlers

I am writing my first jQuery plugin which is a tree browser. It shall first show the top level elements and on click go deeper and show (depending on level) the children in a different way.
I got this up and running already. But now I want to implement a "back" functionality and for this I need to store an array of clicked elements for each instance of the tree browser (if multiple are on the page).
I know that I can put instance private variables with "this." in the plugin.
But if I assign an event handler of the onClick on a topic, how do I get this instance private variable? $(this) is referencing the clicked element at this moment.
Could please anyone give me an advise or a link to a tutorial how to get this done?
I only found tutorial for instance specific variables without event handlers involved.
Any help is appreciated.
Thanks in advance.
UPDATE: I cleaned out the huge code generation and kept the logical structure. This is my code:
(function ($) {
$.fn.myTreeBrowser = function (options) {
clickedElements = [];
var defaults = {
textColor: "#000",
backgroundColor: "#fff",
fontSize: "1em",
titleAttribute: "Title",
idAttribute: "Id",
parentIdAttribute: "ParentId",
levelAttribute: "Level",
treeData: {}
};
var opts = $.extend({}, $.fn.myTreeBrowser.defaults, options);
function getTreeData(id) {
if (opts.data) {
$.ajax(opts.data, { async: false, data: { Id: id } }).success(function (resultdata) {
opts.treeData = resultdata;
});
}
}
function onClick() {
var id = $(this).attr('data-id');
var parentContainer = getParentContainer($(this));
handleOnClick(parentContainer, id);
}
function handleOnClick(parentContainer, id) {
if (opts.onTopicClicked) {
opts.onTopicClicked(id);
}
clickedElements.push(id);
if (id) {
var clickedElement = $.grep(opts.treeData, function (n, i) { return n[opts.idAttribute] === id })[0];
switch (clickedElement[opts.levelAttribute]) {
case 1:
renderLevel2(parentContainer, clickedElement);
break;
case 3:
renderLevel3(parentContainer, clickedElement);
break;
default:
debug('invalid level element clicked');
}
} else {
renderTopLevel(parentContainer);
}
}
function getParentContainer(elem) {
return $(elem).parents('div.myBrowserContainer').parents()[0];
}
function onBackButtonClick() {
clickedElements.pop(); // remove actual element to get the one before
var lastClickedId = clickedElements.pop();
var parentContainer = getParentContainer($(this));
handleOnClick(parentContainer, lastClickedId);
}
function renderLevel2(parentContainer, selectedElement) {
$(parentContainer).html('');
var browsercontainer = $('<div>').addClass('myBrowserContainer').appendTo(parentContainer);
//... rendering the div ...
// for example like this with a onClick handler
var div = $('<div>').attr('data-id', element[opts.idAttribute]).addClass('fct-bs-col-md-4 pexSubtopic').on('click', onClick).appendTo(subtopicList);
// ... rendering the tree
var backButton = $('<button>').addClass('btn btn-default').text('Back').appendTo(browsercontainer);
backButton.on('click', onBackButtonClick);
}
function renderLevel3(parentContainer, selectedElement) {
$(parentContainer).html('');
var browsercontainer = $('<div>').addClass('myBrowserContainer').appendTo(parentContainer);
//... rendering the div ...
// for example like this with a onClick handler
var div = $('<div>').attr('data-id', element[opts.idAttribute]).addClass('fct-bs-col-md-4 pexSubtopic').on('click', onClick).appendTo(subtopicList);
// ... rendering the tree
var backButton = $('<button>').addClass('btn btn-default').text('Back').appendTo(browsercontainer);
backButton.on('click', onBackButtonClick);
}
function renderTopLevel(parentContainer) {
parentContainer.html('');
var browsercontainer = $('<div>').addClass('fct-page-pa fct-bs-container-fluid pexPAs myBrowserContainer').appendTo(parentContainer);
// rendering the top level display
}
getTreeData();
//top level rendering! Lower levels are rendered in event handlers.
$(this).each(function () {
renderTopLevel($(this));
});
return this;
};
// Private function for debugging.
function debug(debugText) {
if (window.console && window.console.log) {
window.console.log(debugText);
}
};
}(jQuery));
Just use one more class variable and pass this to it. Usually I call it self. So var self = this; in constructor of your plugin Class and you are good to go.
Object oriented way:
function YourPlugin(){
var self = this;
}
YourPlugin.prototype = {
constructor: YourPlugin,
clickHandler: function(){
// here the self works
}
}
Check this Fiddle
Or simple way of passing data to eventHandler:
$( "#foo" ).bind( "click", {
self: this
}, function( event ) {
alert( event.data.self);
});
You could use the jQuery proxy function:
$(yourElement).bind("click", $.proxy(this.yourFunction, this));
You can then use this in yourFunction as the this in your plugin.

JS Hooking Custom Events

I have a client side script for my .NET control. The script hooks all the :checkbox items to trigger an event called OnOptionItemSelected.
function ControlScript() {
this.OnOptionItemSelected = function (e) { /* do something */ }
this.Configure = function () {
// hook change event of items in the input control
$('#' + this.ControlID).find(":radio, :checkbox")
.on("change", this, this.OnOptionItemSelected);
}
}
(Some pieces of code removed)
In another place on the page, I get the client side script for a particular control and need to hook the OnOptionItemSelected event. How would I hook this event?
var script = GetScriptForControl(ID);
if (script)
// hook script.OnOptionItemSelected to a custom function ????
I will suppose that the GetScriptForControl return a instance of ControlScript. So what i would do is:
if(script) {
oldOptionItemSelected = script.OnOptionItemSelected
script.OnOptionItemSelected = function(ev) {
/* before OptionItemSelected code HERE */
oldOptionItemSelected.call(script,ev);
/* after OptionItemSelected code HERE */
};
/* rebind the events */
script.Configure();
}
I actually found an answer that better suits my needs:
function ControlClientSide()
{
this.CheckBoxClicked = function() { }
this.Configure = function() {
$('#checkBox100').change(function() { $(this).triggerHandler("CheckBoxClicked"); }.bind(this));
};
this.Configure();
};
var x = new ControlClientSide();
$(x).on("CheckBoxClicked", function() { alert( this.constructor.name + ' Hi'); })
.on("CheckBoxClicked", function() { alert( this.constructor.name + ' Hi2'); });
The only thing I cant figure out is how to get "this" to be the checkbox in the "CheckBoxClicked" event handlers. Currently it is set to the ControlClientSide function.
See example: http://jsfiddle.net/KC5RH/3/

javascript combining public and private window.onload

i am currently learning about javascript namespaces as i build a website and i have the following requirements: i want to make all of my code private so that other public scripts on the page (possibly adverts, i'm not too sure at this stage) cannot override or alter my javascript. the problem i am foreseeing is that the public scripts may use window.onload and i do not want them to override my private version of window.onload. i do still want to let them run window.onload though.
so far i have the following layout:
//public code not written by me - i'm thinking this will be executed first
window.onload = function() {
document.getElementById('pub').onclick = function() {
alert('ran a public event');
};
};
//private code written by me
(function() {
var public_onload = window.onload; //save the public for later use
window.onload = function() {
document.getElementById('priv').onclick = function() {
a = a + 1
alert('ran a private event. a is ' + a);
};
};
if(public_onload) public_onload();
var a = 1;
})();
i have quite a few questions about this...
firstly, is this a good structure for writing my javascript code, or is there a better one? (i'm planning on putting all of my code within the anonymous function). is my private code really private, or is there a way that the public javascript can access it? i'm guessing the answer to this is "yes - using tricky eval techniques. do not embed code you do not trust", but i'd like to know how this would be done if so.
secondly, when i click on the public link, the event is not fired. why is this?
finally, if i comment out the if(public_onload) public_onload(); line then a is returned correctly when i click the private button. but if i leave this line in then a's value is nan. why is this?
You can attach event listeners to avoid their overriding in some way like this:
<ol id="res"></ol>
<script type="text/javascript">
var res = document.getElementById('res');
function log(line) {
var li = document.createElement('li');
li.innerHTML = line;
res.appendChild(li);
}
// global code:
window.onload = function() {
log('inside the global window.onload handler');
};
// private code:
(function(window) {
function addEvent(el, ev, fn) {
if (el.addEventListener) {
el.addEventListener(ev, fn, false);
} else if (el.attachEvent) {
el.attachEvent('on' + ev, fn);
} else {
el['on' + ev] = fn;
}
}
addEvent(window, 'load', function() {
log('inside the second window.onload handler in "private section"');
});
})(window);
</script>​
DEMO
The example of code organization you asked about:
HTML:
<ol id="res"></ol>​
JavaScript:
/* app.js */
// in global scope:
var MyApp = (function(app) {
var res = document.getElementById('res');
app.log = function(line) {
var li = document.createElement('li');
li.innerHTML = line;
res.appendChild(li);
};
app.doWork = function() {
app.log('doing a work');
};
return app;
})(MyApp || {});
/* my-app-module.js */
// again in global scope:
var MyApp = (function(app) {
app.myModule = app.myModule || {};
app.myModule.doWork = function () {
app.log('my module is doing a work');
};
return app;
})(MyApp || {});
/* somewhere after previous definitions: */
(function() {
MyApp.doWork();
MyApp.myModule.doWork();
})();
​DEMO
MyApp is accessible from outside
Nothing is accessible from outside

Categories

Resources