HTML & CSS
Before we go on to the JavaScript, let’s take a look at the HTML and CSS for the image carousel which will be used to demonstrate the Swiper plugin. The HTML is shown below.<div style="width: 330px; height: 200px;">
<div id="target">
<div>
<div><img alt="" src="rexy.jpg" /></div>
<div><img alt="" src="xena.jpg" /></div>
<div><img alt="" src="xenaagain.jpg" /></div>
<div><img alt="" src="rexyagain.jpg" /></div>
</div>
</div>
</div>
Similarly, the carousel’s CSS is shown below.
img { /*100% width to scale the height proportionately*/
width: 100%;
margin: 0;
}
.frame {
width: 100%;
height: 100%;
border: 1px solid #ccc;
overflow: hidden;
position: relative;
}
.pictures {
position: absolute;
width: 400%; /*change accordingly*/
left: 0%;
}
.pictures:after {
content: "\0020";
display: none;
height: 0;
}
.pictures .pic {
width: 25%; /*change with respect to .pictures*/
float: left;
}
The inner container (.pictures
) is set to 400% to contain four images. Each image’s container (.pic
) is set to 25% so that the images end up at a width of 330 pixels. If you change the number of images or use absolute values instead of percentages, you’d want to change the width value of the .pictures
and .pic
elements accordingly.
The images are made to line up horizontally by floating to the left. The frame (.frame
) is made to show only one image at a time. With this setup, we can then “slide” the carousel by changing the left
property of the .pictures
<div>
element.
JavaScript
Here is the skeleton of the plugin:(function ($) {
'use strict';
var Swiper = function (el, callbacks) {
}
$.fn.swiper = function (callbacks) {
if (typeof callbacks.swiping !== 'function') {
throw '"swiping" callback must be defined.';
}
this.each(function () {
var tis = $(this),
swiper = tis.data('swiper');
if (!swiper) { //i.e. plugin not invoked on the element yet
tis.data('swiper', (swiper = new Swiper(this, callbacks)));
}
});
};
}(jQuery));
This listing is boilerplate code for creating a jQuery plugin. The bulk of the complexity is handled by the internal class Swiper
, whose methods are not yet defined. Swiper
is responsible for reading the events produced by the browser and invoking the callback. The plugin is defined in a closure so that the Swiper
class will not be mistakenly overridden by external code. The plugin is also prevented from binding to an element more than one time by associating the instantiated Swiper
class with the swiper
data attribute.
var Swiper = function (el, callbacks) {
var tis = this;
this.el = el;
this.cbs = callbacks;
this.points = [0, 0];
//perform binding
this.el.addEventListener('touchstart', function (evt) {
tis.start(evt);
});
this.el.addEventListener('touchmove', function (evt) {
evt.preventDefault();
tis.move(evt);
});
};
In the above listing, the Swiper
constructor instantiates the object’s properties and event handlers. The points
property is a two-celled array that stores the starting position of the finger in the first cell, and the ending position in the second cell. We will see the usage of this array in subsequent listings. Its values are both initially zero.
The constructor binds the touchstart
and touchmove
events, and proxies the events to the corresponding methods in the Swiper
class. The touchstart
binding initializes the points
array with the initial position of the finger. The touchmove
binding gives us the movement of the finger, which we will pass to the callback function to offset the carousel accordingly.
Swiper.prototype.start = function (evt) {
if (evt.targetTouches && evt.targetTouches.length === 1) {
if (evt.targetTouches[0].offsetX) {
this.points[0] = evt.targetTouches[0].offsetX;
} else if (evt.targetTouches[0].layerX) {
this.points[0] = evt.targetTouches[0].layerX;
} else {
this.points[0] = evt.targetTouches[0].pageX;
}
//make initial contact with 0 difference
this.points[1] = this.points[0];
}
};
The listing above shows the start()
method, which takes the event and reads the set of touches generated on screen. In devices with multi-touch capability, which means nearly all modern smartphones and tablets, this property is an array storing the locations of all contact points with the screen. In this implementation, we are keeping track of one contact point as we are tracking a single swipe gesture which is done using one finger.
We are checking for the different properties of the touch event to accommodate the different implementations of the touch behaviour on different devices. This used to be required to make it work for different devices
. Today, however, the devices that I have tested all generate the pageX
property.
Since we are checking only for a horizontal swipe gesture, we ignore the pageY
property. We also set the cells of the points
property to the same value so that the initial difference between the starting and ending points is zero.
The function binding for the touchmove
event and other helper methods are listed below.
Swiper.prototype.diff = function () {
return this.points[1] - this.points[0];
};
Swiper.prototype.move = function (evt) {
if (evt.targetTouches && evt.targetTouches.length === 1) {
if (evt.targetTouches[0].offsetX) {
this.points[1] = evt.targetTouches[0].offsetX;
} else if (evt.targetTouches[0].layerX) {
this.points[1] = evt.targetTouches[0].layerX;
} else {
this.points[1] = evt.targetTouches[0].pageX;
}
this.cbs.swiping(this.diff());
this.points[0] = this.points[1];
}
};
The diff()
method simply calculates the difference between the last point (which changes as the user moves the finger) and the previous point. This is illustrated by the following figure.
The move()
method also checks through the different properties to get the right one for storing into the second cell of the points
property. After storing the value, the callback function is invoked with the difference between the previous position and the new position of the finger. The callback function is responsible for changing the position of the carousel. This is explained below.
After invoking the callback, the previous position’s value is replaced with the current position’s value. The next time the callback is invoked, the difference will be the displacement between the current position and the previous position instead of the starting position. This is required if we want the movement of the carousel to mirror that of the finger. Without this line, the carousel’s movement accumulates the difference and the result is a large displacement of images in response to a small movement of the finger, which is clearly undesirable for a smooth user experience.
The listing below invokes the plugin.
var target = $('#target'),
pictures = $('.pictures', target),
MAX_LEFT = -990,
MAX_RIGHT = 0,
currPos = 0,
cb = {
swiping: function (displacement) {
currPos += displacement;
if (currPos > MAX_RIGHT) {
currPos = MAX_RIGHT;
} else if (currPos < MAX_LEFT) {
currPos = MAX_LEFT;
}
pictures.css('left', currPos + 'px');
}
};
target.swiper(cb);
We get the element using its id
. We also need a handle to the .pictures
element within the target because the carousel’s positioning is changed by changing the left
CSS property of this element.
We set the left and right limit of the carousel’s position with the MAX_LEFT
and MAX_RIGHT
variables. These values have to change in relation to the carousel’s size. They are used so that the user does not scroll the carousel to empty spaces. The MAX_RIGHT
variable determines how far right the finger can drag the carousel to hit the left-most image. Naturally, this value is 0
. The MAX_LEFT
variable limits how far left the finger can move the carousel. Since there are four images, to display the right-most one, the three images on the left have to be displaced. The values are derived like this:
330 (width of one image) * 3 = 990
We also have a variable, currPos
, that stores the current position of the carousel. Alternatively, we can get the position of the carousel like so:
currPos = parseInt(pictures.css('left'));
The preferred approach is to use the variable. The only reason is that of performance – retrieving the left
property of the element and converting it into an integer definitely consumes more processing power than accessing a variable’s value. This is cognizant of the fact that we are adding behavior on top of a browser’s interface, so it is important that we keep the plugin lean.
The callback is specified as a property within a JSON literal. Why not simply pass it as a function? Well, this is to set the stage for part two of this series, where we will explain how to add swipe gesture detection to the plugin.
A final note: on iOS devices (iPhones and iPads), there is a bouncing effect on the browser window when you scroll the carousel. This is apparent if the carousel is near the bottom or the top (as is the case here) of the page. To prevent that from happening, we call the preventDefault()
method on the touchmove
event. Incidentally, by calling the preventDefault()
method, it prevents the event from bubbling up the DOM hierarchy, which in turn leads to better performance, especially apparent on slower devices such as the Nexus One. I’ve tested the plugin on the iPad 2 (iOS 6.0.1), Nexus One (Android 2.3.6), and Galaxy Note II (Android 4.1.2). If you have used any other devices/OS, feel free to let us know in the comments!
Frequently Asked Questions (FAQs) about jQuery Plugin for Touch Swiping
How Can I Implement jQuery Plugin for Touch Swiping in My Project?
To implement the jQuery plugin for touch swiping in your project, you first need to include the jQuery library in your HTML file. After that, you can include the jQuery touch swipe plugin. Once these are included, you can use the swipe function to detect swipe events on any element of your choice. You can then define what should happen when a swipe event is detected.
What Are the Different Types of Swipe Events That Can Be Detected?
The jQuery touch swipe plugin can detect four types of swipe events: swipe, swipeLeft, swipeRight, and swipeUp, swipeDown. Each of these events corresponds to the direction of the swipe. You can define different actions for each type of swipe event.
Can I Customize the Swipe Function?
Yes, the swipe function is highly customizable. You can set the swipe threshold (the minimum distance required for a swipe), the maximum time allowed for a swipe, and the swipe event target. You can also define what should happen when a swipe event is detected.
How Can I Test If the Swipe Function Is Working Correctly?
You can test the swipe function by using a touch-enabled device or an emulator. Simply swipe on the element where you have applied the swipe function and see if the defined action is performed.
Can I Use the jQuery Touch Swipe Plugin with Other jQuery Plugins?
Yes, the jQuery touch swipe plugin can be used in conjunction with other jQuery plugins. However, you need to ensure that there are no conflicts between the plugins.
Is the jQuery Touch Swipe Plugin Compatible with All Browsers?
The jQuery touch swipe plugin is compatible with most modern browsers. However, it may not work properly in older browsers that do not support touch events.
Can I Use the jQuery Touch Swipe Plugin in a Mobile App?
Yes, the jQuery touch swipe plugin can be used in mobile apps. It can detect touch events on both Android and iOS devices.
How Can I Debug Issues with the jQuery Touch Swipe Plugin?
You can debug issues with the jQuery touch swipe plugin by using the console log function. This will display any errors or issues in the browser’s console.
Can I Use the jQuery Touch Swipe Plugin for Multi-Touch Gestures?
The jQuery touch swipe plugin is primarily designed for single touch gestures. However, it can be extended to support multi-touch gestures with some additional coding.
How Can I Learn More About the jQuery Touch Swipe Plugin?
You can learn more about the jQuery touch swipe plugin by reading the documentation, looking at examples, and experimenting with the plugin on your own. You can also ask questions on forums and communities dedicated to jQuery and web development.
Chee How is a technology enthusiast and is especially interested in all Web related stuff. He currently works as a Web developer in a company he co-founded, and writes JavaScript, Java and PHP on a day-to-day basis.