Here is the example.
I create an object that have 3 elements referenced by the first parameter (document.querySelectorAll...) the I loop through those elements and launch a specific scrolling function (simplified in the example. Inside this function I use this approach to detect the scroll and save perf. I pass the element every time I call this function (first commented console.log) and works well, then inside the timer also I still having the 3 elements (in this example) but then inside the if statement I only have the first one.
I think I understand the problem, but I'm not finding a solution.
function extend( a, b ) {
for( var key in b ) {
if( b.hasOwnProperty( key ) ) {
a[key] = b[key];
}
}
return a;
}
function addEvent(element, evnt, funct){
if (element.attachEvent)
return element.attachEvent('on'+evnt, funct);
else
return element.addEventListener(evnt, funct, false);
}
function MyObject(el, opt){
this.el = el;
this.opt = extend( {}, this.opt );
extend( this.opt, opt );
this._init();
}
MyObject.prototype._init = function(){
var self = this;
//this.didScroll = false; //Remove this and put it per element
this.totalObjs = document.querySelectorAll(this.el);
this.objs = [].slice.call( this.totalObjs );
this.objs.forEach( function( el, i ) {
self._onScroll(el);
});
};
MyObject.prototype._onScroll = function(e){
var self = this;
e.didScroll = false; // Add per element
addEvent(window, 'scroll', function(){
e.didScroll = true;
});
// Here I have my 3 elements
// console.log(e.id);
setInterval(function() {
// Here I have my 3 elements
// console.log(e.id);
if( e.didScroll ){
e.didScroll = false;
// Here I have only the first element
console.log(e.id);
}
}, 500);
};
var obj1 = new MyObject('.obj', {});
The problem is your scope. You are setting an interval for each element but checking inside that interval from the main object and setting the didScroll variable to false on the first one. Set this variable on the element and it works just fine.
function extend( a, b ) {
for( var key in b ) {
if( b.hasOwnProperty( key ) ) {
a[key] = b[key];
}
}
return a;
}
function addEvent(element, evnt, funct){
if (element.attachEvent)
return element.attachEvent('on'+evnt, funct);
else
return element.addEventListener(evnt, funct, false);
}
function MyObject(el, opt){
this.el = el;
this.opt = extend( {}, this.opt );
extend( this.opt, opt );
this._init();
}
MyObject.prototype._init = function(){
var self = this;
//this.didScroll = false; //Remove this and put it per element
this.totalObjs = document.querySelectorAll(this.el);
this.objs = [].slice.call( this.totalObjs );
this.objs.forEach( function( el, i ) {
self._onScroll(el);
});
};
MyObject.prototype._onScroll = function(e){
var self = this;
e.didScroll = false; // Add per element
addEvent(window, 'scroll', function(){
e.didScroll = true;
});
// Here I have my 3 elements
// console.log(e.id);
setInterval(function() {
// Here I have my 3 elements
// console.log(e.id);
if( e.didScroll ){
e.didScroll = false;
// Here I have only the first element
console.log(e.id);
}
}, 500);
};
var obj1 = new MyObject('.obj', {});
Related
I am trying to write a jQuery method which watches for changes of inputs inside a given form element:
(function($) {
$.fn.filter = function(options) {
console.log('Outside');
var self = this;
var settings = $.extend({}, options);
this.on('change', ':input', function(e) {
console.log('Inside');
$(self).serialize(); // Here is the problem
});
return this;
}
})(jQuery);
$('#filter-form').filter();
When I use $(self).serialize();, the function being called again. I expect that the 'Outside' part only runs once on initialization and not every time the input of the form changes.
I do not understand what is happening here. I'd appreciate if someone could explain to me why this is happening!
The issue is that you are redefining jQuery's filter method, which it internally uses in its serialize method. If you change the name, it will work. The definition of serialize is shown below:
jQuery.fn.extend( {
serialize: function() {
return jQuery.param( this.serializeArray() );
},
serializeArray: function() {
return this.map( function() {
// Can add propHook for "elements" to filter or add form elements
var elements = jQuery.prop( this, "elements" );
return elements ? jQuery.makeArray( elements ) : this;
} )
.filter( function() {
var type = this.type;
// Use .is( ":disabled" ) so that fieldset[disabled] works
return this.name && !jQuery( this ).is( ":disabled" ) &&
rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
( this.checked || !rcheckableType.test( type ) );
} )
.map( function( _i, elem ) {
var val = jQuery( this ).val();
if ( val == null ) {
return null;
}
if ( Array.isArray( val ) ) {
return jQuery.map( val, function( val ) {
return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
} );
}
return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
} ).get();
}
} );
Working Example:
(function($) {
$.fn._filter = function(options) {
console.log('Outside');
var self = this;
var settings = $.extend({}, options);
this.on('change', ':input', function(e) {
console.log('Inside');
$(self).serialize();
});
return this;
}
})(jQuery);
$('#filter-form')._filter();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form id="filter-form">
<input type="text">
</form>
I have a function that is working and looks like this:
var $$ = function(selector, context) {
context = context || document;
var elements = context.querySelectorAll(selector);
return Array.prototype.slice.call(elements);
}
// My function
var myFunction = function() {
$$( '.my-selector' ).forEach( function( element ) {
element.addEventListener('click', function(e){
console.log('Do something');
});
});
}
I would prefer it looks more like jQuery:
// My function
var myFunction = function() {
$('.my-selector').click(function(e) {
console.log('Do something');
});
}
I can't figure out how to do that. Any ideas?
(I want my code to be independent of frameworks so I don't want jQuery)
Solution1: with your own wrapper: Fiddler link
var $$ = function(selector, context) {
context = context || document;
var elements = context.querySelectorAll(selector);
var wrapper = function(elem){
var element = elem;
this.click = function(cb){
element.forEach(function(el){
el.addEventListener('click', function(e){
cb.apply(element,[e]);
});
});
};
};
return new wrapper(Array.prototype.slice.call(elements));
}
$$("#test").click(function(e){
console.log("Do something:"+e.type);
});
Solution 2 directly binding to native element: Fiddler link
var $$ = function(selector, context) {
context = context || document;
var elements = Array.prototype.slice.call(context.querySelectorAll(selector));
elements.click = function(cb){
elements.forEach(function(el){
el.addEventListener('click', function(e){
cb.apply(elements,[e]);
});
});
};
return elements;
}
$$("#test").click(function(e){
console.log("Do something:"+e.type);
});
I have a javascript file that contains the following objects and functions........
;( function( window ) {
'use strict';
function extend( a, b ) {
for( var key in b ) {
if( b.hasOwnProperty( key ) ) {
a[key] = b[key];
}
}
return a;
}
// taken from https://github.com/inuyaksa/jquery.nicescroll/blob/master/jquery.nicescroll.js
function hasParent( e, id ) {
if (!e) return false;
var el = e.target||e.srcElement||e||false;
while (el && el.id != id) {
el = el.parentNode||false;
}
return (el!==false);
}
// returns the depth of the element "e" relative to element with id=id
// for this calculation only parents with classname = waypoint are considered
function getLevelDepth( e, id, waypoint, cnt ) {
cnt = cnt || 0;
if ( e.id.indexOf( id ) >= 0 ) return cnt;
if( classie.has( e, waypoint ) ) {
++cnt;
}
return e.parentNode && getLevelDepth( e.parentNode, id, waypoint, cnt );
}
// returns the closest element to 'e' that has class "classname"
function closest( e, classname ) {
if( classie.has( e, classname ) ) {
return e;
}
return e.parentNode && closest( e.parentNode, classname );
}
function mlPushMenu( el, trigger, options ) {
this.el = el;
this.trigger = trigger;
this.options = extend( this.defaults, options );
// support 3d transforms
this.support = Modernizr.csstransforms3d;
if( this.support ) {
this._init();
}
}
mlPushMenu.prototype = {
defaults : {
// overlap: there will be a gap between open levels
// cover: the open levels will be on top of any previous open level
type : 'overlap', // overlap || cover
// space between each overlaped level
levelSpacing : 40,
// classname for the element (if any) that when clicked closes the current level
backClass : 'mp-back'
},
_init : function() {
// if menu is open or not
this.open = false;
// level depth
this.level = 0;
// the moving wrapper
this.wrapper = document.getElementById( 'mp-pusher' );
// the mp-level elements
this.levels = Array.prototype.slice.call( this.el.querySelectorAll( 'div.mp-level' ) );
// save the depth of each of these mp-level elements
var self = this;
this.levels.forEach( function( el, i ) { el.setAttribute( 'data-level', getLevelDepth( el, self.el.id, 'mp-level' ) ); } );
// the menu items
this.menuItems = Array.prototype.slice.call( this.el.querySelectorAll( 'li' ) );
// if type == "cover" these will serve as hooks to move back to the previous level
this.levelBack = Array.prototype.slice.call( this.el.querySelectorAll( '.' + this.options.backClass ) );
// event type (if mobile use touch events)
this.eventtype = mobilecheck() ? 'touchstart' : 'click';
// add the class mp-overlap or mp-cover to the main element depending on options.type
classie.add( this.el, 'mp-' + this.options.type );
// initialize / bind the necessary events
this._initEvents();
},
// close the menu
_resetMenu : function() {
this._setTransform('translate3d(0,0,0)');
this.level = 0;
// remove class mp-pushed from main wrapper
classie.remove( this.wrapper, 'mp-pushed' );
this._toggleLevels();
this.open = false;
},
// add to global namespace
window.mlPushMenu = mlPushMenu;
} )( window );
The question I have is how do I call the object _resetMenu in another script. to my poor knowledge it should be ......
window.mlPushMenu._resetMenu();
that should execute that object in my mind but it isn't working so clealy I am wrong ... Any help here would be much appreciated..
this is the example of the button I have created thus far.....
$('.iconM-referrals').on('click', function () {
window.mlPushMenu._resetMenu();
$("#colorscreen").remove();
$("body").append('<div id="colorscreen" class="animated"></div>');
$("#colorscreen").addClass("fadeInUpBig");
$('.fadeInUpBig').css('background-color', 'rgba(13,135,22,0.3)');
The way you have mlPushMenu set up is as a Constructor, not as a stand-alone module. (See: Any tutorial on Object-Oriented programming) You'd need to construct an instance variable to call the function. For instance:
myInstanceOfPushMenu = new mlPushMenu();
myInstanceOfPushMenu._resetMenu();
This, however, is assuming that the script inclusion and declaration, and anything left out of your question, is all set up properly.
I'm trying to build a jQuery Plugin using there Name spacing as per there direction here:
http://docs.jquery.com/Plugins/Authoring#Namespacing
Now i have run into a problem my plugin need to use a setTimeout to fire one of its methods,
var jScrollerMethods = {
ready:function(){
return this.each(function(){
var self = this,
$this = $(this),
data = $this.data('jScroller');
settings.timeoutHandle = setTimeout(function(){
if(settings.direction = "left"){
this.moveLeft();
}else if(settings.direction = "right"){
this.moveRight();
}
}, settings.time);
$this.data('jScroller', {
settings: settings,
element: this
});
});
}
$.fn.jScroller = function(call){
if ( jScrollerMethods[call] ) {
return jScrollerMethods[call].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof call === 'object' || ! call ) {
if (call) { $.extend(settings, call); }
return jScrollerMethods.init.apply( this, call );
} else {
$.error( 'Method ' + call + ' does not exist on jQuery.jScroller' );
}
}
but as I though would happen setTimeout is fired from the Window not the plugin object and being that I want the plugin to be usable on more than once per page so I can't just save the current object to the window how can I achieve this?
I found out that when using this outside of the return this.each will give you the exact selector results
So with some work i have managed to do it,
var jScrollerMethods = {
ready:function(){
selector = this;
return this.each(function(){
var self = this,
$this = $(this),
data = $this.data('jScroller');
settings.timeoutHandle = setTimeout(function(){
if(settings.direction = "left"){
selector.jScroller("moveLeft");
}else if(settings.direction = "right"){
selector.jScroller("moveRight");
}
}, settings.time);
$this.data('jScroller', {
settings: settings,
element: this
});
});
}
$.fn.jScroller = function(call){
if ( jScrollerMethods[call] ) {
return jScrollerMethods[call].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof call === 'object' || ! call ) {
if (call) { $.extend(settings, call); }
return jScrollerMethods.init.apply( this, call );
} else {
$.error( 'Method ' + call + ' does not exist on jQuery.jScroller' );
}
}
I am trying to be able to tab through these tooltip as a part of my tab order on my page but the tabbing stops at the first tooltip. Is the problem with the try catch statement in my Javascript code?
landingTooltip = {
locked :false,
initialize: function(){
if (dojo.byId('pickup-cycle')){
this.buttons = dojo.query('.button-link', dojo.byId('pickup- cycle'));
}
else{
return;
}
var _this = this;
dojo.forEach(
_this.buttons,
function(obj){
Hoverable.disconnect(obj)
domExtender.connect(obj,"onmouseenter",function(e){
_this.show(domExtender.closest(obj, '.step'), obj);
});
// dojo.connect(obj,"onmouseleave",function(e){
// _this.hide(domExtender.closest(obj, '.step'), obj, null);
// });
domExtender.connect(dojo.query('a', obj)[0],"onfocus",function(e){
_this.show(domExtender.closest(obj, '.step'), obj);
});
}
);
},
show: function(el, t){
if (this.locked == true){
return;
}
this.locked = true;
var t = t;
var _this = this;
dojo.addClass(dojo.query('span', t)[0], 'hover');
this.tt = dojo.query('.tool-tip', el)[0];
var placed = dojo.place(
_this.tt,
dojo.body(),
'first'
);
dojo.style(_this.tt, 'display', 'block');
_this.tt.focus();
var setFocus = dojo.query('h5', placed)[0];
setFocus.focus();
this.inst = dojo.connect(_this.tt,"onblur",function(e){
if (domExtender.closest(e.target, 'div.tool-tip') == null) {
_this.hide(domExtender.closest(t, '.step'), t, true);
}
});
this.inst1 = dojo.connect(dojo.body(), 'onclick',function(e){
//alert(domExtender.closest(e.target, 'div.tool-tip'));
if (domExtender.closest(e.target, 'div.tool-tip') == null){
_this.hide(domExtender.closest(t, '.step'), t, true);
}
});
},
hide: function(el, t, blur){
// if (this.locked == true){
// return;
// }
this.locked = true;
var _this = this;
if (this.inst){
dojo.disconnect(_this.inst);
}
if (this.inst1){
dojo.disconnect(_this.inst1);
}
dojo.removeClass(dojo.query('span', t)[0], 'hover');
var placed = dojo.place(
_this.tt,
el,
'first'
);
dojo.style(placed, 'display', 'none');
_this.locked = false;
try {
var setFocus = domExtender.next(el);
setFocus.focus();
} catch (e) {
}
}
}
Install firebug
Set a breakpoint
See what exactly is causing tab order to break.
Report a new specific question here.