Why is my swiper undefined? - javascript

I cannot understand why this is happening. I have a swiper nested in another swiper (one for vertical scrolling, the other for horizontal. What is driving me nuts is the nested swiper is not defined when I need to destroy it. Here is what I am doing:
function embedSwiper(){
var embeddedEcosystem = new Swiper('.swiper-nested', {
//Styling set in bootstrap.min.css for pagination
mode: 'vertical',
pagination: '.pagination-nested',
paginationClickable: true,
simulateTouch : true,
});
embeddedEcosystem.swipeTo(0, 500, false);
return embeddedEcosystem;
}
That creates the swiper, and returns it to this function:
function reInitEmbedded(){
setTimeout(function(){
embed = embedSwiper();
$(".swiper-nested").css({"height" : "0px"});
$(".swiper-nested").css({"height" : $(".internal-relations").height()});
useLocalImg();
embed.reInit();
//Set the slide height properly since it never works as intended
return embed;
}, 600);
}
I need to set the height here otherwise the slide is not properly fitted (and yes I have tried calculate height, but that was giving me issues on mobile since I am using worklight)
Now, here is where stuff gets wonkey. I am testing this in chrome (sorry, no link that I can provide you with at the moment).
//Resize cards
swiper = reinitSwiper();
innerSwiper = reInitEmbedded();
//Show info
detailsTransition();
//Hides the overlay, and empties the panels to be filled next time
//Bound calls for use when needed
$(".back-button.btn.btn-primary.pull-left").on('click', function(){
goBack(lookUpName);
innerSwiper.destroy();
swiper.destroy();
});
As you can see, I have the swiper variable, which works, and can be destroyed normally, and I have the innerSwiper. The rest is irrelevant because it was working prior to this. What is driving me nuts is that innerSwiperkeeps coming up as undefined, but it shouldn't be because I have traced the stack call in chrome's debugger and the returns all have the swiper variable for the inner swiper. So my question is: What am I doing wrong that I keep getting undefined?

This is because of scoping issue.
inside the click handler, the scope is changed and it does not have access to innerSwiper. So, do this instead:
this.swiper = reinitSwiper();
this.innerSwiper = reInitEmbedded();
var self = this; // HOLD REFERNECE TO THIS AS SELF
//Show info
detailsTransition();
//Hides the overlay, and empties the panels to be filled next time
//Bound calls for use when needed
$(".back-button.btn.btn-primary.pull-left").on('click', function(){
goBack(lookUpName);
self.innerSwiper.destroy(); // here you use self that has reference to swiper
self.swiper.destroy();
});
One more mistake as Thom x pointed out. fix it like this:
// Make sure self is defined be before this function
function reInitEmbedded(){
setTimeout(function(){
embed = embedSwiper();
$(".swiper-nested").css({"height" : "0px"});
$(".swiper-nested").css({"height" : $(".internal-relations").height()});
useLocalImg();
embed.reInit();
//Set the slide height properly since it never works as intended
self.innerSweeper = embed; // change this
}, 600);
}

reInitEmbedded is not returning any value.
function reInitEmbedded(){
setTimeout(function(){
return true;
}, 600);
}
var a = reInitEmbedded();
console.log(a);
==> undefined

Ok, so I figured out what I was doing wrong. Due to the fact that swiper and innersSwiper were global, and I didn't realize that setTimeout can't return anything, I had to access the global variables directly. And since i wasn't using the word innerSwiper in the reInitEmbedded, it wasn't referencing properly.

Related

How to fix the syntax error about ";" of jQuery code below

I want to fade in and fade out one button in my web page using jQuery library. The problem is that the code below is not executing because of some syntax errors.
$(document).ready(y);
var y=function(){
$('div').mouseenter(z);
$('div').mouseleave(a);
};
var z=function(){
$('div').fadeTo('fast',1);
};
var a=function(){
$('div').fadeTo('fast',0.5);
};
Should anyone tell me what I am doing wrong here?
Variable and functions gets hosted to the top
this
$(document).ready(y);
var y = function(){
$('div').mouseenter(z);
$('div').mouseleave(a);
};
becomes like this when parsed by the browser
var y = undefined
$(document).ready(y); // undefined
// redefines y
y = function(){
$('div').mouseenter(z);
$('div').mouseleave(a);
};
so declering y first or chage it to a function helps
$(document).ready(y); // function
function y(){
$('div').mouseenter(z);
$('div').mouseleave(a);
};
EDIT:
I feel like you are doing an awful lot of work to fade in/out a button, try this instead?
$(document).ready(function(){
$('#button_id').fadeIn(1000);
$('#button_id').fadeOut(1000);
//fade in/out on the button click
$('#button_id').click(function(){
$('#button_id').fadeIn(1000);
$('#button_id').fadeOut(1000);
});
//the value parse into the fade methods will execute the animation over 1 second
});

How to make sure that all events are unattached when called multiple times in Javascript?

I'm using dragswipe, a plugin from here. It works perfectly fine. However, I have a requirement to do dynamic carousels. So, every time a user changes something on a page, the carousels get updated dynamically. I thought I could just recall the plugin to update the element, but somehow when I dragged the carousel the functional gets called multiple times.
For example, I do this to initialise the plugin.
function init() {
$('#carousel').dragswipe({
width: 320,
current_page_element: '#current_page',
total_pages_element:'#total_pages'
});
}
So after the page is updated via ajax I have a callback to call this method again like this
function callback() {
init();
}
Everything should is updated perfectly fine, but when I started dragging. The carousel skips some pages. I thought I had to unbind all the events so I tried this. $('#carousel').unbind() but the problem still persists.
Here's the source code when the plugin is initialised.
$.fn.dragswipe = function(options) {
options = $.extend({
offset: 0,
turn_threshold: 0.1,
current_page: 0
},options)
this.each(function() {
option = $(this).hammer({
drag_vertical: false,
swipe_time: 20
});
// start a drag.
// if we're moving left or right, set our initial distance to 0.
$(this).bind('dragstart',function(ev) {
console.log('dragstart');
if (ev.direction=='left' || ev.direction=='right') {
drag_distance = 0;
}
});
// while dragging, change the dragging distance based on how far a user has dragged.
$(this).bind('drag',function(ev) {
console.log('drag');
if (ev.direction=='left' || ev.direction=='right') {
drag_distance = ev.distanceX;
var options = CONFIGS[$(this).data('dragswipe_id')];
$(this).updateOffset(options.offset+drag_distance);
}
});
$(this).bind('dragend',function(ev) {
console.log('dragend');
if (ev.direction=='left' || ev.direction=='right') {
var options = CONFIGS[$(this).data('dragswipe_id')];
if (Math.abs(drag_distance / options.width) > options.turn_threshold) {
if (ev.direction=='left') {
options.current_page++;
}
if (ev.direction=='right') {
options.current_page--;
}
}
// store modified options
CONFIGS[$(this).data('dragswipe_id')] = options;
console.log(options.current_page);
$(this).gotoPage(options.current_page,true);
}
});
// set the dragswipe ID used to look up config options later.
$(this).data('dragswipe_id',CONFIGS.length);
// store config options.
CONFIGS[$(this).data('dragswipe_id')] = options;
});
}
Which I see nothing wrong with the plugin, but maybe I'm missing something obvious.
UPDATED
I have created the example in jsfiddle, but it's not working just in case anyone can fix the problem. Also, on the plugin site itself. The problem can be reproduce by running this code to initialise the plugin multiple times. After running the code twice, when you drag the page it goes to the last page instead of the second page.
$('#carousel').dragswipe({
width: 320,
current_page_element: '#current_page',
total_pages_element:'#total_pages'
});
Have you tried turning it off and on again?
Dragswipe has a removeDragswipe() method that does a little bit more than just unbinding, and it also explicitly only unbinds events that are related to dragswipe itself so it won't mess up any other events that may have been bound.
So instead of unbind(), do this:
function callback() {
$('#carousel').removeDragswipe();
init();
}
As discussed in the comments, this doesn't work because it seems that even after unbinding all the events, after re-binding them by activating the plugin again the events seem to fire an extra time.
What does seem to be a workaround is to actually rebuild the entire element so that no events can linger:
function callback() {
$('#carousel').removeDragswipe();
rebuildCarousel();
init();
}
function rebuildCarousel() {
var wrapper = $('#carousel').parent;
var html = parent.html();
$('#carousel').remove();
wrapper.html(html);
}

Separate desktop and mobile jQuery functions on a responsive site

I'm working on a responsive site with a specific set of jQuery functions for the desktop layout and mobile layout. They interfere with each other if they're both active at the same time.
By checking window.width, I'm able to deliver only the correct set of functions on page load, and I'd like to do the same on window.resize.
I've set up a stripped down Fiddle of where I'm at here: http://jsfiddle.net/b9XEj/
Two problems exist right now:
Either desktopFunctions or mobileFunctions will continuously fire on page resize, whether they have already been loaded or not.
If the window is resized beyond one breakpoint and then returned to the previous size, the incorrect set of functions will already have been loaded, interfering with the current set.
The window.resize function should behave in the following way:
Check if the correct set of functions currently active for the viewport size
If yes, return.
If no, fire correct set of functions and remove incorrect set of functions if they exist.
In the Fiddle example above, you would always see a single line, displaying either "Mobile Functions are active" or "Desktop Functions are active".
I'm a bit lost at this point, but I have tried using
if ($.isFunction(window.mobileFunctions))
to check if functions already exist, but I can't seem to get it working without breaking the overall function. Here's a fiddle for that code: http://jsfiddle.net/nA8TB/
Thinking ahead, this attempt also wouldn't take into account whether the incorrect set of functions exists already. So, I'm really hoping there's a way I can deal with this in a simpler way and solve both problems.
Any help would be much appreciated!
Following conquers 2 of the problems. The resize fires many times a second, so using a timeout will fix it firing your code constantly. It also adds a check to see if the same size is in effect, and return if it is
$(document).ready(function() {
var windowType;
var $wind = $(window);
var desktopFunctions = function() {
$('body').append('<p>Desktop functions are active</p>');
}
var mobileFunctions = function() {
$('body').append('<p>Mobile Functions are active</p>');
}
var mobileCheck = function() {
var window_w = $wind.width();
var currType = window_w < 940 ? 'mobile' :'desktop';
if (windowType == currType) {
$('body').append('<p>No Type Change, Width= '+window_w+'</p>');
return;
} else {
windowType = currType;
}
if (windowType == 'mobile') {
mobileFunctions();
} else {
desktopFunctions();
}
}
mobileCheck();
var resizeTimer;
$wind.resize(function() {
if (resizeTimer) {
clearTimeout(resizeTimer);
}
resizeTimer = setTimeout(mobileCheck, 300)
});
});
DEMO: http://jsfiddle.net/b9XEj/1/
Without seeing some real world differences between your 2 sets of functions it is hard to provide gudance on how to stop them conflicting. One possibility is checking the windowType in your functions
You can prevent the continuous firing by adding a delay mobileCheck. Use a setTimeout along with a checkPending boolean value.
var checkPending = false;
$(window).resize(function(){
if (checkPending === false) {
checkPending = true;
setTimeout(mobileCheck, 1000);
}
});
See here: http://jsfiddle.net/2Q3pT/
Edit
As far as the second requirement, you could use this pattern to create or use the existing one:
mobileFunctions = mobileFunctions || function() {
// mobile functions active
};
See: http://jsfiddle.net/2Q3pT/2/

Namespace issue when calling function in one .js file from another .js file

In an attempt to make a swipe-able slideshow for many images, I am attempting to stitch together two plugins, Swipe.js and lazyloader.js. I want to have the slideshow timer event from the Swipe plugin call the update function inside of Lazyloader. From researching, it seems my problem appears to be one of namespace; that is, Swipe doesn't know about the stuff inside Lazyloader, and therefore cannot call the function.
Given the following code, am I understanding my problem correctly?
If so, how can I get Swipe to access Lazyloader's functions?
Thank you.
FOLLOW UP:
After Q&A with Cheeso, I see now that I was asking the wrong question in regard to what I ultimately needed to happen. I add this comment to avoid having any future readers getting confused because of my bad question. This is not about namespace or even about accessing a private function, which shouldn't be done. This thread is utimately about how inadvisable it may be to try and frankenstein libraries together if you don't really know what you're doing. ; )
END FOLLOW UP
The callback in swipe.js:
this.callback = this.options.callback || function() {
the script called inside the html:
<script src="js/jquery.js" type="text/javascript"></script>
<script src="js/jquery.lazyload.js" type="text/javascript"></script>
<script src='js/swipe.js' type="text/javascript"></script>
<script type="text/javascript">$(document).ready(function() {
function myAlert(){
//call a function from inside jquery.lazyload.js
alertMe();
}
//instantiate a swipe object and pass in a bunch of
//options, especially "callback" which fires on every slide change
window.mySwipe = new Swipe(document.getElementById('slider1'), {
startSlide: 0,
speed: 1000,
auto: 5000,
callback: function(){
myAlert();
}
});
// run lazyload on every VISIBLE image with the class "lazy" on load, and on every click
$("img.lazy").lazyload({event: "click", effect : "fadeIn", threshold: 0,});
});
</script>
jquery.lazyloader.js:
//outside of the container below, I can be called by myAlert()
function alertMe() {
alert("it worked!");
}
(function($, window) {
$window = $(window);
$.fn.lazyload = function(options) {
///
///Here be a bunch of lazyloader stuff
///
//I work when above, but once I am inside this container, I cannot be called by myAlert(),
//and I will error as 'not a valid function'
function alertMe() {
alert("it worked! Even inside of this container! ");
}
})(jQuery, window);
Could be as simple as this: to set the callback option on the Swipe instance to point to a function that does LazyLoader things. This is just a guess, because I don't know swipe, but I looked at the code dump and saw that callback function; it appears to be the key point of extension for Swipe.
If I'm correct, then it's not a namespace issue at all. It's just what you said - knitting together disparate libraries or modules. Typically a js module will have an extension point or two that allows this. I'm guessing Swipe's extension point is that callback.
Maybe something like this:
function myFn() {
// do whatever lazyload thing you want to do, here.
}
$(document).ready(function() {
var slider1 = new Swipe(document.getElementById('slider1'),
{startSlide: 0,
speed: 1000,
auto: 10000,
callback:myFn});
$("img.lazy").lazyload({event: "click"});
});
You may have to refactor to make either slider1 or some other variables global, so they are accessible outside of the doc.ready function.
FOLLOWUP
You have this:
<script type="text/javascript">
$(document).ready(function() {
function myAlert(){
//call a function from inside jquery.lazyload.js
alertMe();
}
//instantiate a swipe object and pass in a bunch of
//options, especially "callback" which fires on every slide change
window.mySwipe = new Swipe(document.getElementById('slider1'), {
startSlide: 0,
speed: 1000,
auto: 5000,
callback: function(){
myAlert();
}
});
$("img.lazy").lazyload({event: "click", effect : "fadeIn", threshold: 0,});
});
</script>
There are several problems with that code. It should be closer to this:
<script type="text/javascript">
function myThing(){
// ** Do something useful here. This may involve calling
// ** other functions. Those functions need not be defined
// ** within jquery.lazyload.js.
}
$(document).ready(function() {
//instantiate a swipe object and pass in a bunch of
//options, especially "callback" which fires on every slide change.
// ** There is no need, as far as I can tell, to assign the
// ** output to a global variable. (window.mySwipe). In fact, given
// ** the code you have, there is no need to retain the output at all.
new Swipe(document.getElementById('slider1'), {
startSlide: 0,
speed: 1000,
auto: 5000,
// ** Just use the function name. You don't need to define
// ** a new anonymous function to wrap around it.
callback: myThing
});
$("img.lazy").lazyload({event: "click", effect : "fadeIn", threshold: 0,});
});
</script>
MORE
I suppose that what you want is to lazily load the "next few" images so
that they are available as the user swipes through a list. From what I
read (briefly), you need to call lazyload() only once, for all images
on the page. If your images are inside a "container", then specify the
container option on lazyload. (See
http://www.appelsiini.net/projects/lazyload) The plugin then monitors
the viewport for that container and loads images as necessary. There is
no need to call "lazyload" again, no need for a callback.
If this is
not working, then Swipe and jquery.lazyload.js may not be usable together,
because of assumptions made in lazyload. In that case, you can still
get lazy-loading, but you need to do it yourself.
To do that, eliminate lazyload.js completely. Then, in the Swipe callback, do what you would like lazyload to have done: set the src attribute on the "next"
image. Setting the attribute will cause the webpage to load the
image. To get lazy loading, in the original state the src attributes on all
images must be blank, or set to some fixed image URL, before the swiping
begins. Now, you may ask: in the callback how will you know which image to load?
And which URL to set into the src attribute? That is up for you to
determine. I presume there is some way to get the appropriate
information from Swipe in that callback.
If you don't understand this, then you need some instruction or some
further reading to establish the basic understanding of how browsers
handle images. From my reading, you do not need Swipe to call
lazyload's update().
Some general suggestions:
for any module, you should not try to call functions defined
internal to that module. They are internal for a reason.
for any module, you should not modify its source code unless you are
very certain you know what you are doing. If you are poking around and
trying things, put the code in your own module. Your own javascript code belongs in the web page, or in a separate module referenced by the webpage.
you should probably not be defining functions within
$(document).ready() unless you have a good reason.
read the documentation. I had never heard of Swipe or lazyload before
today. The suggestions I made here about them - specifically, regarding the
callback on Swipe, and the container option on lazyload - came from me
reading the doc for how to use them.

SetInterval inside scrolling events on iPhone - Why this code doesn't work?

Please, take a look at this code (I'm using Zepto http://zeptojs.com/ BTW)...
var timer = false;
$(window).bind('touchstart touchmove scroll', function (e) {
if (timer === false) {
timer = setInterval(function () {
$('footer').css('top', (this.pageYOffset + this.innerHeight - 40) + 'px');
console.log('Adjusted...');
}, 100);
}
}).bind('touchend', function () {
clearInterval(timer);
timer = false;
console.log('Cleaned it up...');
});
As you can see, I have a footer element that I'm trying to keep fixed on the bottom of the iPhone screen. I know that there are libraries that helps us make this quite easily like iScroll 4 http://cubiq.org/iscroll-4, but I was trying to see if I could make it simpler.
It turns out that the code above doesn't work properly. While I'm actually scrolling the page, for some reason setInterval doesn't execute but instead seems to pile up on the background to run every call at the same time.
At the end it doesn't do what I wanted it to do, which is to "animate" the footer and have it in place during scroll not only after. Does anyone has any idea on how such effect could be achieved on some similar manner?
Thanks!
When you pass a method to setInterval() (or any other function, for that matter), it will be invoked with a wrong this value. This problem is explained in detail in the JavaScript reference.
MDC docs
Inside your outer callback, this will be the DOM element you care about, but inside the setInterval callback, this will be window. Keep in mind that this is a keyword, not a variable, and that it is highly context sensitive.
The usual approach is to capture the value of this in a variable and then use that variable instead of this:
if(timer === false) {
var self = this; // "_that" is also a common name for the variable.
timer = setInterval(function () {
$('footer').css('top', (self.pageYOffset + self.innerHeight - 40) + 'px');
console.log('Adjusted...');
}, 100);
}
Similar issues apply to all callbacks in JavaScript, always make sure you know what this is and grab its value and build a closure over that value when it won't be what you want.

Categories

Resources