How do I move the parent element when swiping over an iframe? - javascript

I have a slider that detects swipes via the touchmove event and moves the content accordingly. However when there is an iframe in the content, and I slide my finger over the iframe, the page won't move because the touchmove event is intercepted by the iframe itself, not the parent page. Thus the parent div doesn't move.
This iframe also needs to remain clickable since it is an ad so I can't just cover it with another div that has a higher z-index.
What can I do in Javascript to bubble the touchmove event up to the parent? They are on the same domain. Any help would be appreciated!
The slider I am using is this - http://dezignhero.github.io/swiper.js/ (which works fine when there are no iframes in the pages)
var Swiper = function(selector, options) {
/*------- Globals -------*/
var viewportWidth = 0,
frameWidth = 0,
animating = false,
numSlides = 0,
limitEnd = 0,
goTo = 0,
currentSlide = 0,
orientation = 0;
// Swiping
var swipe = {
started : false,
startX : 0,
endX : 0,
at : 0,
strength : 0
};
// Settings
var settings = {
ease : 0.3,
swipeMin : 40,
preventAdvance : false,
container : '.container',
frame : '.page',
frameWidth : false, // accepts a number in pixels
controls : '.control',
clickEvent : 'click',
updateEvent : 'update',
controlsOnly : false,
};
/*------- Handles -------*/
var el = selector,
$parent = $(el),
$container, $controls, $frame, $prevCtrl, $nextCtrl;
/*------- Methods -------*/
var init = function(options) {
// Exit if element doesn't exist
if ( $(el).length == 0 ) return;
// Merge settings
settings = $.extend(settings, options || {});
// Initialize handles
$container = $(settings.container, el);
$controls = $(settings.controls, el);
$frame = $(settings.frame, el);
// Assign Ids to frames
$frame.each(function(i){
$(this).attr('data-id', i);
numSlides++;
});
// Add initial class
$($frame.selector+'[data-id=0]', el).addClass('current');
// Set Dimensions
resize();
// Setup CSS
$container.css({
'-webkit-transition' : 'all '+settings.ease+'s ease-out',
'-webkit-transform' : 'translate3d(0,0,0)', // Performance optimization, put onto own layer for faster start
'left' : 0
});
// Monitoring controls if they exist
if ( $controls.length > 0 ) {
// Determine whether or not to use click event
if ('ontouchstart' in document.documentElement) {
settings.clickEvent = 'touchstart';
}
// Create handlers
$prevCtrl = $(settings.controls+'[data-action=prev]');
$nextCtrl = $(settings.controls+'[data-action=next]');
// Bind behavior
$controls.on(settings.clickEvent, function(){
var self = $(this),
action = self.attr('data-action');
// Ensure action defined
if ( typeof action == 'undefined' ) return;
if ( action == 'next' && currentSlide < numSlides - 1 ) {
goTo = currentSlide + 1;
} else if ( action == 'prev' && currentSlide > 0 ) {
goTo = currentSlide - 1;
}
// Move container
jumpTo(goTo);
});
}
// Display controls correctly
if ( settings.preventAdvance ) {
disableSliding();
} else {
updateControls();
}
// Swiping
if ( !settings.controlsOnly ) {
$container[0].addEventListener('touchstart', function(e) { touchStart(e); }, false);
$container[0].addEventListener('touchmove', function(e) { touchMove(e); }, false);
$container[0].addEventListener('touchend', function(e) { touchEnd(e); }, false);
// Desktop
$container[0].addEventListener('mousedown', function(e) { touchStart(e); }, false);
$container[0].addEventListener('mousemove', function(e) { if (e.which==1) { touchMove(e); } }, false);
$container[0].addEventListener('mouseup', function(e) { touchEnd(e); }, false);
}
// Prevent anchor tags from getting in the way
$('a', el).on('touchstart click', function(){
return swipe.started ? false : true;
});
// Prevent image dragging on getting in the way
$('img', el).on('dragstart', function(){
return false;
});
// Check if Android
var ua = navigator.userAgent.toLowerCase(),
isAndroid = ua.indexOf("android") > -1;
// Orientation Change
var supportsOrientationChange = "onorientationchange" in window,
orientationEvent = (supportsOrientationChange && !isAndroid) ? "orientationchange" : "resize";
// Listener for orientation changes
window.addEventListener(orientationEvent, function() {
// Prevent 'fake' orientation calls
if ( orientation != window.orientation ) {
orientation = window.orientation;
resize(function(){
jumpTo(currentSlide);
});
}
}, false);
},
resize = function(callback){
viewportWidth = $parent.width();
frameWidth = ( settings.frameWidth ) ? settings.frameWidth : viewportWidth;
// Apply new sizes
$frame.width(frameWidth);
$container.width(frameWidth*numSlides);
// Set end limit
limitEnd = settings.frameWidth ? viewportWidth/frameWidth : numSlides;
// callback
if ( typeof callback == 'function' ) {
callback();
}
},
touchStart = function(e) {
swipe.at = getPosition(); // for touch move
// Get start point
swipe.startX = e.touches ? e.touches[0].pageX : e.pageX;
swipe.startY = e.touches ? e.touches[0].pageY : e.pageY;
swipe.endX = swipe.startX; // prevent click swiping when touchMove doesn't fire
},
touchEnd = function(e) {
swipe.started = false;
// Nullify event
e.preventDefault();
if ( animating ) return;
var moved = swipe.endX - swipe.startX,
threshold = frameWidth / 3;
goTo = currentSlide;
// Figure out closest slide
if ( Math.abs(moved) > threshold || swipe.strength > settings.swipeMin ) {
if ( moved > 0 && currentSlide > 0 ) {
goTo--;
} else if ( moved < 0 && currentSlide < limitEnd-1 ) {
goTo++;
}
}
// Jump to closest
jumpTo(goTo);
},
touchMove = function(e) {
if ( !$parent.hasClass('disabled') ) {
swipe.started = true;
var touchX = e.touches ? e.touches[0].pageX : e.pageX,
touchY = e.touches ? e.touches[0].pageY : e.pageY,
dX = touchX - swipe.startX,
dY = touchY - swipe.startY;
swipe.strength = Math.abs(touchX - swipe.endX);
swipe.endX = touchX;
// Escape if motion wrong
if ( Math.abs(dX) < Math.abs(dY) ) return;
e.preventDefault();
// Always run this so that hit the ends
animate(swipe.at+dX, false);
}
},
getPosition = function() {
// Get current point and Stay there
var style = document.defaultView.getComputedStyle($container[0], null),
transform = new WebKitCSSMatrix(style.webkitTransform);
// Return position based on direction
return transform.m41;
},
animate = function(scrollTo, ease) {
// Momentum Effect or Not
$container[0].style.webkitTransition = ( ease ) ? 'all '+settings.ease+'s ease-out' : 'none';
$container[0].style.webkitTransform = 'translate3d('+scrollTo+'px,0,0)';
// Allow animating again
if ( ease ) {
animating = true;
window.setTimeout(function(){
animating = false;
}, settings.ease*1000);
}
},
jumpTo = function(num, ease) {
// Keep within range
if ( num >= 0 && num < limitEnd ) {
// Animate
var hasEase = ( typeof ease !== 'undefined' ) ? ease : true;
animate(-num*frameWidth, hasEase);
// If new slide
if ( num != currentSlide ) {
// Update current slide
currentSlide = num;
// Update current slide
$frame.removeClass('current');
$($frame.selector+'[data-id='+currentSlide+']').addClass('current');
// Update parent to trigger update event and new slide
$parent.trigger(settings.updateEvent, [ currentSlide, Math.floor(limitEnd) ]);
// Control Buttons
updateControls();
// Disable Again
if ( settings.preventAdvance ) {
disableSliding();
}
}
}
},
updateControls = function() {
// Only run if controls exist
if ( $controls.length == 0 ) return;
if ( currentSlide >= 0 && currentSlide < limitEnd ) {
$controls.show();
if ( currentSlide == 0 ) {
$prevCtrl.hide();
} else if ( currentSlide == limitEnd-1 ) {
$nextCtrl.hide();
}
} else {
$controls.hide();
}
},
disableSliding = function() {
// Hide Controls
$('.control', el).hide();
// Add disabled flag
$parent.addClass('disabled');
},
enableSliding = function() {
// Enable control buttons
updateControls();
// Remove disabled flag
$parent.removeClass('disabled');
};
// Initialize the object
init(options);
return {
element : $parent,
jumpTo : jumpTo,
swiping : function() {
return swipe.started;
},
disableSliding : disableSliding,
enableSliding : enableSliding,
status : function() {
return {
'current' : currentSlide+1,
'total' : numSlides
}
},
next : function() {
jumpTo(currentSlide+1);
},
prev : function() {
jumpTo(currentSlide-1);
}
};
}

The problem is that the iframe swallows touch events. So on the area that you are swiping if there is any iframe, you wont be able to swipe. The only solution I know, is to cover the iframe with a transparent div and dispatch any click events to the iframe. This way touchmove event will be enabled on the iframe.
I have created a basic jquery plugin to do that here.
https://gist.github.com/agaase/6971953
Usage
$(selector).coverIframes();
This will cover any iframes inside $(selector) with a transparent div and transfer any click events.

I know it's an old question but you can use:
iframe {
pointer-events: none;
}

Related

Ruby on Rails 6 custom JavaScript file Uncaught ReferenceError: Rellax is not defined

Descriptions of issue
It supposes to add Parallax effect on .rellax elements according to https://github.com/dixonandmoe/rellax
If you are familiar with Ruby on Rails 6 and Webpacker, would you please explain why rails can't read rellax.js properly and what can I do to make it work? Thank you!
Terminal
$ rails s
=> Booting Puma
=> Rails 6.0.3.2 application starting in development
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.5 (ruby 2.6.6-p146), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://127.0.0.1:3000
* Listening on tcp://[::1]:3000
Use Ctrl-C to stop
Started GET "/" for ::1 at 2020-07-15 18:34:48 -0700
(1.0ms) SELECT sqlite_version(*)
Processing by WelcomeController#index as HTML
Rendering welcome/index.html.erb within layouts/application
[Webpacker] Everything's up-to-date. Nothing to do
Rendered welcome/index.html.erb within layouts/application (Duration: 4.8ms | Allocations: 2428)
[Webpacker] Everything's up-to-date. Nothing to do
Completed 200 OK in 47ms (Views: 39.7ms | ActiveRecord: 0.0ms | Allocations: 13263)
index.html.erb
<h1>Welcome#index</h1>
<p>Find me in app/views/welcome/index.html.erb</p>
<h4>data-rellax-speed = default</h4>
<section>
<div class="col">
<br>With Percentage (0.5) <br><br>
<div id="21" class="container"><div class="block">#1<span class="rellax" data-rellax-percentage="0.5">#1</span></div></div>
<div id="22" class="container"><div class="block">#2<span class="rellax" data-rellax-percentage="0.5">#2</span></div></div>
<div id="23" class="container"><div class="block">#3<span class="rellax" data-rellax-percentage="0.5">#3</span></div></div>
<div id="24" class="container"><div class="block">#4<span class="rellax" data-rellax-percentage="0.5" style="transition: transform 10s cubic-bezier(0,1,.5,1);">#4</span></div></div>
<div id="25" class="container"><div class="block">#5<span class="rellax" data-rellax-percentage="0.5" style="transition: transform 10s cubic-bezier(0,1,.5,1);">#5</span></div></div>
<div id="26" class="container"><div class="block">#6<span class="rellax" data-rellax-percentage="0.5" style="transition: transform 10s cubic-bezier(0,1,.5,1);">#6</span></div></div>
</div>
<div class="col">
<br>Without Percentage <br><br>
<div id="21" class="container"><div class="block">#1<span class="rellax" style="transition: transform 10s cubic-bezier(0,1,.5,1);">#1</span></div></div>
<div id="22" class="container"><div class="block">#2<span class="rellax" style="transition: transform 10s cubic-bezier(0,1,.5,1);">#2</span></div></div>
<div id="23" class="container"><div class="block">#3<span class="rellax" style="transition: transform 10s cubic-bezier(0,1,.5,1);">#3</span></div></div>
<div id="24" class="container"><div class="block">#4<span class="rellax">#4</span></div></div>
<div id="25" class="container"><div class="block">#5<span class="rellax">#5</span></div></div>
<div id="26" class="container"><div class="block">#6<span class="rellax">#6</span></div></div>
</div>
</section>
<!-- Scripts -->
<%= javascript_pack_tag 'rellax' %>
<script>
var rellax = new Rellax('.rellax');
</script>
app/javascript/packs/rellax.js
// ------------------------------------------
// Rellax.js
// Buttery smooth parallax library
// Copyright (c) 2016 Moe Amaya (#moeamaya)
// MIT license
//
// Thanks to Paraxify.js and Jaime Cabllero
// for parallax concepts
// ------------------------------------------
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], factory);
} else if (typeof module === 'object' && module.exports) {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory();
} else {
// Browser globals (root is window)
root.Rellax = factory();
}
}(typeof window !== "undefined" ? window : global, function () {
var Rellax = function(el, options){
"use strict";
var self = Object.create(Rellax.prototype);
var posY = 0;
var screenY = 0;
var posX = 0;
var screenX = 0;
var blocks = [];
var pause = true;
// check what requestAnimationFrame to use, and if
// it's not supported, use the onscroll event
var loop = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame ||
function(callback){ return setTimeout(callback, 1000 / 60); };
// store the id for later use
var loopId = null;
// Test via a getter in the options object to see if the passive property is accessed
var supportsPassive = false;
try {
var opts = Object.defineProperty({}, 'passive', {
get: function() {
supportsPassive = true;
}
});
window.addEventListener("testPassive", null, opts);
window.removeEventListener("testPassive", null, opts);
} catch (e) {}
// check what cancelAnimation method to use
var clearLoop = window.cancelAnimationFrame || window.mozCancelAnimationFrame || clearTimeout;
// check which transform property to use
var transformProp = window.transformProp || (function(){
var testEl = document.createElement('div');
if (testEl.style.transform === null) {
var vendors = ['Webkit', 'Moz', 'ms'];
for (var vendor in vendors) {
if (testEl.style[ vendors[vendor] + 'Transform' ] !== undefined) {
return vendors[vendor] + 'Transform';
}
}
}
return 'transform';
})();
// Default Settings
self.options = {
speed: -2,
verticalSpeed: null,
horizontalSpeed: null,
breakpoints: [576, 768, 1201],
center: false,
wrapper: null,
relativeToWrapper: false,
round: true,
vertical: true,
horizontal: false,
verticalScrollAxis: "y",
horizontalScrollAxis: "x",
callback: function() {},
};
// User defined options (might have more in the future)
if (options){
Object.keys(options).forEach(function(key){
self.options[key] = options[key];
});
}
function validateCustomBreakpoints () {
if (self.options.breakpoints.length === 3 && Array.isArray(self.options.breakpoints)) {
var isAscending = true;
var isNumerical = true;
var lastVal;
self.options.breakpoints.forEach(function (i) {
if (typeof i !== 'number') isNumerical = false;
if (lastVal !== null) {
if (i < lastVal) isAscending = false;
}
lastVal = i;
});
if (isAscending && isNumerical) return;
}
// revert defaults if set incorrectly
self.options.breakpoints = [576, 768, 1201];
console.warn("Rellax: You must pass an array of 3 numbers in ascending order to the breakpoints option. Defaults reverted");
}
if (options && options.breakpoints) {
validateCustomBreakpoints();
}
// By default, rellax class
if (!el) {
el = '.rellax';
}
// check if el is a className or a node
var elements = typeof el === 'string' ? document.querySelectorAll(el) : [el];
// Now query selector
if (elements.length > 0) {
self.elems = elements;
}
// The elements don't exist
else {
console.warn("Rellax: The elements you're trying to select don't exist.");
return;
}
// Has a wrapper and it exists
if (self.options.wrapper) {
if (!self.options.wrapper.nodeType) {
var wrapper = document.querySelector(self.options.wrapper);
if (wrapper) {
self.options.wrapper = wrapper;
} else {
console.warn("Rellax: The wrapper you're trying to use doesn't exist.");
return;
}
}
}
// set a placeholder for the current breakpoint
var currentBreakpoint;
// helper to determine current breakpoint
var getCurrentBreakpoint = function (w) {
var bp = self.options.breakpoints;
if (w < bp[0]) return 'xs';
if (w >= bp[0] && w < bp[1]) return 'sm';
if (w >= bp[1] && w < bp[2]) return 'md';
return 'lg';
};
// Get and cache initial position of all elements
var cacheBlocks = function() {
for (var i = 0; i < self.elems.length; i++){
var block = createBlock(self.elems[i]);
blocks.push(block);
}
};
// Let's kick this script off
// Build array for cached element values
var init = function() {
for (var i = 0; i < blocks.length; i++){
self.elems[i].style.cssText = blocks[i].style;
}
blocks = [];
screenY = window.innerHeight;
screenX = window.innerWidth;
currentBreakpoint = getCurrentBreakpoint(screenX);
setPosition();
cacheBlocks();
animate();
// If paused, unpause and set listener for window resizing events
if (pause) {
window.addEventListener('resize', init);
pause = false;
// Start the loop
update();
}
};
// We want to cache the parallax blocks'
// values: base, top, height, speed
// el: is dom object, return: el cache values
var createBlock = function(el) {
var dataPercentage = el.getAttribute( 'data-rellax-percentage' );
var dataSpeed = el.getAttribute( 'data-rellax-speed' );
var dataXsSpeed = el.getAttribute( 'data-rellax-xs-speed' );
var dataMobileSpeed = el.getAttribute( 'data-rellax-mobile-speed' );
var dataTabletSpeed = el.getAttribute( 'data-rellax-tablet-speed' );
var dataDesktopSpeed = el.getAttribute( 'data-rellax-desktop-speed' );
var dataVerticalSpeed = el.getAttribute('data-rellax-vertical-speed');
var dataHorizontalSpeed = el.getAttribute('data-rellax-horizontal-speed');
var dataVericalScrollAxis = el.getAttribute('data-rellax-vertical-scroll-axis');
var dataHorizontalScrollAxis = el.getAttribute('data-rellax-horizontal-scroll-axis');
var dataZindex = el.getAttribute( 'data-rellax-zindex' ) || 0;
var dataMin = el.getAttribute( 'data-rellax-min' );
var dataMax = el.getAttribute( 'data-rellax-max' );
var dataMinX = el.getAttribute('data-rellax-min-x');
var dataMaxX = el.getAttribute('data-rellax-max-x');
var dataMinY = el.getAttribute('data-rellax-min-y');
var dataMaxY = el.getAttribute('data-rellax-max-y');
var mapBreakpoints;
var breakpoints = true;
if (!dataXsSpeed && !dataMobileSpeed && !dataTabletSpeed && !dataDesktopSpeed) {
breakpoints = false;
} else {
mapBreakpoints = {
'xs': dataXsSpeed,
'sm': dataMobileSpeed,
'md': dataTabletSpeed,
'lg': dataDesktopSpeed
};
}
// initializing at scrollY = 0 (top of browser), scrollX = 0 (left of browser)
// ensures elements are positioned based on HTML layout.
//
// If the element has the percentage attribute, the posY and posX needs to be
// the current scroll position's value, so that the elements are still positioned based on HTML layout
var wrapperPosY = self.options.wrapper ? self.options.wrapper.scrollTop : (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
// If the option relativeToWrapper is true, use the wrappers offset to top, subtracted from the current page scroll.
if (self.options.relativeToWrapper) {
var scrollPosY = (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
wrapperPosY = scrollPosY - self.options.wrapper.offsetTop;
}
var posY = self.options.vertical ? ( dataPercentage || self.options.center ? wrapperPosY : 0 ) : 0;
var posX = self.options.horizontal ? ( dataPercentage || self.options.center ? self.options.wrapper ? self.options.wrapper.scrollLeft : (window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft) : 0 ) : 0;
var blockTop = posY + el.getBoundingClientRect().top;
var blockHeight = el.clientHeight || el.offsetHeight || el.scrollHeight;
var blockLeft = posX + el.getBoundingClientRect().left;
var blockWidth = el.clientWidth || el.offsetWidth || el.scrollWidth;
// apparently parallax equation everyone uses
var percentageY = dataPercentage ? dataPercentage : (posY - blockTop + screenY) / (blockHeight + screenY);
var percentageX = dataPercentage ? dataPercentage : (posX - blockLeft + screenX) / (blockWidth + screenX);
if(self.options.center){ percentageX = 0.5; percentageY = 0.5; }
// Optional individual block speed as data attr, otherwise global speed
var speed = (breakpoints && mapBreakpoints[currentBreakpoint] !== null) ? Number(mapBreakpoints[currentBreakpoint]) : (dataSpeed ? dataSpeed : self.options.speed);
var verticalSpeed = dataVerticalSpeed ? dataVerticalSpeed : self.options.verticalSpeed;
var horizontalSpeed = dataHorizontalSpeed ? dataHorizontalSpeed : self.options.horizontalSpeed;
// Optional individual block movement axis direction as data attr, otherwise gobal movement direction
var verticalScrollAxis = dataVericalScrollAxis ? dataVericalScrollAxis : self.options.verticalScrollAxis;
var horizontalScrollAxis = dataHorizontalScrollAxis ? dataHorizontalScrollAxis : self.options.horizontalScrollAxis;
var bases = updatePosition(percentageX, percentageY, speed, verticalSpeed, horizontalSpeed);
// ~~Store non-translate3d transforms~~
// Store inline styles and extract transforms
var style = el.style.cssText;
var transform = '';
// Check if there's an inline styled transform
var searchResult = /transform\s*:/i.exec(style);
if (searchResult) {
// Get the index of the transform
var index = searchResult.index;
// Trim the style to the transform point and get the following semi-colon index
var trimmedStyle = style.slice(index);
var delimiter = trimmedStyle.indexOf(';');
// Remove "transform" string and save the attribute
if (delimiter) {
transform = " " + trimmedStyle.slice(11, delimiter).replace(/\s/g,'');
} else {
transform = " " + trimmedStyle.slice(11).replace(/\s/g,'');
}
}
return {
baseX: bases.x,
baseY: bases.y,
top: blockTop,
left: blockLeft,
height: blockHeight,
width: blockWidth,
speed: speed,
verticalSpeed: verticalSpeed,
horizontalSpeed: horizontalSpeed,
verticalScrollAxis: verticalScrollAxis,
horizontalScrollAxis: horizontalScrollAxis,
style: style,
transform: transform,
zindex: dataZindex,
min: dataMin,
max: dataMax,
minX: dataMinX,
maxX: dataMaxX,
minY: dataMinY,
maxY: dataMaxY
};
};
// set scroll position (posY, posX)
// side effect method is not ideal, but okay for now
// returns true if the scroll changed, false if nothing happened
var setPosition = function() {
var oldY = posY;
var oldX = posX;
posY = self.options.wrapper ? self.options.wrapper.scrollTop : (document.documentElement || document.body.parentNode || document.body).scrollTop || window.pageYOffset;
posX = self.options.wrapper ? self.options.wrapper.scrollLeft : (document.documentElement || document.body.parentNode || document.body).scrollLeft || window.pageXOffset;
// If option relativeToWrapper is true, use relative wrapper value instead.
if (self.options.relativeToWrapper) {
var scrollPosY = (document.documentElement || document.body.parentNode || document.body).scrollTop || window.pageYOffset;
posY = scrollPosY - self.options.wrapper.offsetTop;
}
if (oldY != posY && self.options.vertical) {
// scroll changed, return true
return true;
}
if (oldX != posX && self.options.horizontal) {
// scroll changed, return true
return true;
}
// scroll did not change
return false;
};
// Ahh a pure function, gets new transform value
// based on scrollPosition and speed
// Allow for decimal pixel values
var updatePosition = function(percentageX, percentageY, speed, verticalSpeed, horizontalSpeed) {
var result = {};
var valueX = ((horizontalSpeed ? horizontalSpeed : speed) * (100 * (1 - percentageX)));
var valueY = ((verticalSpeed ? verticalSpeed : speed) * (100 * (1 - percentageY)));
result.x = self.options.round ? Math.round(valueX) : Math.round(valueX * 100) / 100;
result.y = self.options.round ? Math.round(valueY) : Math.round(valueY * 100) / 100;
return result;
};
// Remove event listeners and loop again
var deferredUpdate = function() {
window.removeEventListener('resize', deferredUpdate);
window.removeEventListener('orientationchange', deferredUpdate);
(self.options.wrapper ? self.options.wrapper : window).removeEventListener('scroll', deferredUpdate);
(self.options.wrapper ? self.options.wrapper : document).removeEventListener('touchmove', deferredUpdate);
// loop again
loopId = loop(update);
};
// Loop
var update = function() {
if (setPosition() && pause === false) {
animate();
// loop again
loopId = loop(update);
} else {
loopId = null;
// Don't animate until we get a position updating event
window.addEventListener('resize', deferredUpdate);
window.addEventListener('orientationchange', deferredUpdate);
(self.options.wrapper ? self.options.wrapper : window).addEventListener('scroll', deferredUpdate, supportsPassive ? { passive: true } : false);
(self.options.wrapper ? self.options.wrapper : document).addEventListener('touchmove', deferredUpdate, supportsPassive ? { passive: true } : false);
}
};
// Transform3d on parallax element
var animate = function() {
var positions;
for (var i = 0; i < self.elems.length; i++){
// Determine relevant movement directions
var verticalScrollAxis = blocks[i].verticalScrollAxis.toLowerCase();
var horizontalScrollAxis = blocks[i].horizontalScrollAxis.toLowerCase();
var verticalScrollX = verticalScrollAxis.indexOf("x") != -1 ? posY : 0;
var verticalScrollY = verticalScrollAxis.indexOf("y") != -1 ? posY : 0;
var horizontalScrollX = horizontalScrollAxis.indexOf("x") != -1 ? posX : 0;
var horizontalScrollY = horizontalScrollAxis.indexOf("y") != -1 ? posX : 0;
var percentageY = ((verticalScrollY + horizontalScrollY - blocks[i].top + screenY) / (blocks[i].height + screenY));
var percentageX = ((verticalScrollX + horizontalScrollX - blocks[i].left + screenX) / (blocks[i].width + screenX));
// Subtracting initialize value, so element stays in same spot as HTML
positions = updatePosition(percentageX, percentageY, blocks[i].speed, blocks[i].verticalSpeed, blocks[i].horizontalSpeed);
var positionY = positions.y - blocks[i].baseY;
var positionX = positions.x - blocks[i].baseX;
// The next two "if" blocks go like this:
// Check if a limit is defined (first "min", then "max");
// Check if we need to change the Y or the X
// (Currently working only if just one of the axes is enabled)
// Then, check if the new position is inside the allowed limit
// If so, use new position. If not, set position to limit.
// Check if a min limit is defined
if (blocks[i].min !== null) {
if (self.options.vertical && !self.options.horizontal) {
positionY = positionY <= blocks[i].min ? blocks[i].min : positionY;
}
if (self.options.horizontal && !self.options.vertical) {
positionX = positionX <= blocks[i].min ? blocks[i].min : positionX;
}
}
// Check if directional min limits are defined
if (blocks[i].minY != null) {
positionY = positionY <= blocks[i].minY ? blocks[i].minY : positionY;
}
if (blocks[i].minX != null) {
positionX = positionX <= blocks[i].minX ? blocks[i].minX : positionX;
}
// Check if a max limit is defined
if (blocks[i].max !== null) {
if (self.options.vertical && !self.options.horizontal) {
positionY = positionY >= blocks[i].max ? blocks[i].max : positionY;
}
if (self.options.horizontal && !self.options.vertical) {
positionX = positionX >= blocks[i].max ? blocks[i].max : positionX;
}
}
// Check if directional max limits are defined
if (blocks[i].maxY != null) {
positionY = positionY >= blocks[i].maxY ? blocks[i].maxY : positionY;
}
if (blocks[i].maxX != null) {
positionX = positionX >= blocks[i].maxX ? blocks[i].maxX : positionX;
}
var zindex = blocks[i].zindex;
// Move that element
// (Set the new translation and append initial inline transforms.)
var translate = 'translate3d(' + (self.options.horizontal ? positionX : '0') + 'px,' + (self.options.vertical ? positionY : '0') + 'px,' + zindex + 'px) ' + blocks[i].transform;
self.elems[i].style[transformProp] = translate;
}
self.options.callback(positions);
};
self.destroy = function() {
for (var i = 0; i < self.elems.length; i++){
self.elems[i].style.cssText = blocks[i].style;
}
// Remove resize event listener if not pause, and pause
if (!pause) {
window.removeEventListener('resize', init);
pause = true;
}
// Clear the animation loop to prevent possible memory leak
clearLoop(loopId);
loopId = null;
};
// Init
init();
// Allow to recalculate the initial values whenever we want
self.refresh = init;
return self;
};
return Rellax;
}));
Error: Uncaught ReferenceError: Rellax is not defined
I just realized that the package you are trying to import is intended to be included as an AMD or as a node module, without entering in much detail what does that mean is that you should include your module either by using
import Rellax from 'rellax' or let Rellax = require('rellax'), the thing is that by using this kind of import, your packages are not included in the Window objcet, which is the main object where all the global modules and variables are attached, so if you try to access Rellax from outside of the scope where rellax is imported, you are not going to be able to access that object, getting the error message you mentioned:
Rellax is not defined
Because is actually not defined in that scope.
TLDR;
So in order to fix the issue, you have to include the library and use that library in the same scope, you can achieve that by creating a new js file within your pack directory for example:
parallax_init.js
import Rellax from 'rellax'
let rellax = new Rellax('.rellax');
And then include it in your html template using <%= javascript_pack_tag 'parallax_init' %>
Note:
As you are now using webpacker to handle the js dependencies, you have to use yarn to install the packages as webpacker works with yarn.
You can get more information about the JS scope stuff in the AMD documentation: https://requirejs.org/docs/whyamd.html and in the webpack documentation: https://webpack.js.org/concepts/module-federation/
I also recommend you to read the webpacker documentation, which has a bit more information than the rails guide for the moment(Until the rails guide gets updates): https://github.com/rails/webpacker#usage
You can have two approachs to achieve your goal. Install the library or add manually the js files.
If you want to install the library, use yarn (not npm) for that. yarn add rellax. Then just import it in application.js by doing: import 'rellax'; This file is located in app/javascript/packs.
If you want to add the files manually, put these js files in a folder called components or plugins inside app/javascript. Then import all js files in application.js. Having done this correctly, all js files will be available in your views.
It's highly recommended that you read the Rails Guide: The Asset Pipeline to understand how to deal with any kind of assets in rails applications.

WordPress dynamic paging - url conflict

I have downloaded theme which is static html & css template, now I'm trying to convert into wordpress dynamic template. the issue is that the static template is one page website and have some section within it. Scrolling to these section is done by jquery function that uses url to scroll to that specific section like this:
mysite.com/?page=about
so its conflict with main wordpress dynamic paging the website doesn't scroll to that specific section any more.
I've tried to change jquery code in my template but I couldn't fix it, is there a way to change the default wordpress setting in order to stop this conflict?
here the JS code :
(function(window, undefined) {
"use strict";
var Page = (function() {
var $container = $( '#container' ),
// the scroll container that wraps the articles
$scroller = $container.find( 'div.content-scroller' ),
$menu = $container.find( 'aside' ),
// menu links
$links = $menu.find( 'nav > a' ),
$articles = $container.find( 'div.content-wrapper > article' ).not(".noscroll"),
// button to scroll to the top of the page
// only shown when screen size < 715
$toTop = $container.find( 'a.totop-link' ),
// the browser nhistory object
History = window.History,
// animation options
animation = { speed : 800, easing : 'easeInOutExpo' },
// jScrollPane options
scrollOptions = { verticalGutter : 0, hideFocus : true },
// init function
init = function() {
// initialize the jScrollPane on both the menu and articles
_initCustomScroll();
// initialize some events
_initEvents();
// sets some css properties
_layout();
// jumps to the respective chapter
// according to the url
_goto();
},
_initCustomScroll = function() {
// Only add custom scroll to articles if screen size > 715.
// If not the articles will be expanded
if( $(window).width() > 767 ) {
$articles.jScrollPane( scrollOptions );
}
// add custom scroll to menu
$menu.children( 'nav' ).jScrollPane( scrollOptions );
},
_goto = function( chapter ) {
// get the url from history state (e.g. chapter=3) and extract the chapter number
var chapter = chapter || History.getState().url.queryStringToJSON().page,
isHome = ( chapter === undefined ),
// we will jump to the introduction chapter if theres no chapter
$article = $( chapter ? '#' + 'chapter' + chapter : '#' + 'introduction' );
$('#link_introduction').removeClass('active');
$('#link_about').removeClass('active');
$('#link_skills').removeClass('active');
$('#link_experience').removeClass('active');
$('#link_education').removeClass('active');
$('#link_portfolio').removeClass('active');
$('#link_contact').removeClass('active');
$('#link_'+chapter).addClass('active');
if( $article.length ) {
// left / top of the element
var left = $article.position().left,
top = $article.position().top,
// check if we are scrolling down or left
// is_v will be true when the screen size < 715
is_v = ( $(document).height() - $(window).height() > 0 ),
// animation parameters:
// if vertically scrolling then the body will animate the scrollTop,
// otherwise the scroller (div.content-scroller) will animate the scrollLeft
param = ( is_v ) ? { scrollTop : (isHome) ? top : top + $menu.outerHeight( true ) } : { scrollLeft : left },
$elScroller = ( is_v ) ? $( 'html, body' ) : $scroller;
$elScroller.stop().animate( param, animation.speed, animation.easing, function() {
// active class for selected chapter
//$articles.removeClass( 'content-active' );
//$article.addClass( 'content-active' );
} );
}
},
_saveState = function( chapter ) {
// adds a new state to the history object
// this will trigger the statechange on the window
if( History.getState().url.queryStringToJSON().page !== chapter ) {
History.pushState( null, null, '?page=' + chapter );
$('#link_introduction').removeClass('active');
$('#link_about').removeClass('active');
$('#link_skills').removeClass('active');
$('#link_experience').removeClass('active');
$('#link_education').removeClass('active');
$('#link_portfolio').removeClass('active');
$('#link_contact').removeClass('active');
$('#link_'+chapter).addClass('active');
}
},
_layout = function() {
// control the overflow property of the scroller (div.content-scroller)
var windowWidth = $(window).width();
var isipad = navigator.userAgent.toLowerCase().indexOf("ipad");
if(isipad > -1)
{
//$('.totop-link').hide();
switch( true ) {
case ( windowWidth <= 768 ) : $scroller.scrollLeft( 0 ).css( 'overflow', 'visible' ); break;
case ( windowWidth <= 1024 ): $scroller.css( 'overflow-x', 'hidden' ); break;
case ( windowWidth > 1024 ) : $scroller.css( 'overflow', 'hidden' ); break;
};
}
else
{
switch( true ) {
case ( windowWidth <= 768 ) : $scroller.scrollLeft( 0 ).css( 'overflow', 'visible' ); break;
case ( windowWidth <= 1024 ): $scroller.css( 'overflow-x', 'hidden' ); break;
case ( windowWidth > 1024 ) : $scroller.css( 'overflow', 'hidden' ); break;
};
}
},
_initEvents = function() {
_initWindowEvents();
_initMenuEvents();
_initArticleEvents();
},
_initWindowEvents = function() {
$(window).on({
// when resizing the window we need to reinitialize or destroy the jScrollPanes
// depending on the screen size
'smartresize' : function( event ) {
var isipad = navigator.userAgent.toLowerCase().indexOf("ipad");
var ismobile = navigator.userAgent.toLowerCase().indexOf("mobile");
if(isipad > -1 || ismobile > -1 )
{
}
else
{
_layout();
$('article.content').not(".noscroll").each( function() {
var $article = $(this),
aJSP = $article.data( 'jsp' );
if( $(window).width() > 767 ) {
( aJSP === undefined ) ? $article.jScrollPane( scrollOptions ) : aJSP.reinitialise();
_initArticleEvents();
}
else {
// destroy article's custom scroll if screen size <= 715px
if( aJSP !== undefined )
aJSP.destroy();
$container.off( 'click', 'article.content' );
}
});
var nJSP = $menu.children( 'nav' ).data( 'jsp' );
nJSP.reinitialise();
// jumps to the current chapter
_goto();
}
},
// triggered when the history state changes - jumps to the respective chapter
'statechange' : function( event ) {
_goto();
}
});
},
_initMenuEvents = function() {
// when we click a menu link we check which chapter the link refers to,
// and we save the state on the history obj.
// the statechange of the window is then triggered and the page/scroller scrolls to the
// respective chapter's position
$links.on( 'click', function( event ) {
var href = $(this).attr('href'),
chapter = ( href.search(/chapter/) !== -1 ) ? href.substring(8) : 0;
_saveState( chapter );
if (href.indexOf("#") != -1)
return false;
else
return true;
});
// scrolls to the top of the page.
// this button will only be visible for screen size < 715
$toTop.on( 'click', function( event ) {
$( 'html, body' ).stop().animate( { scrollTop : 0 }, animation.speed, animation.easing );
return false;
});
},
_initArticleEvents = function() {
// when we click on an article we check which chapter the article refers to,
// and we save the state on the history obj.
// the statechange of the window is then triggered and the page/scroller scrolls to the
// respective chapter's position
if($(window).width()>768)
{
$container.on( 'click', 'article.content', function( event ) {
var id = $(this).attr('id'),
chapter = ( id.search(/chapter/) !== -1 ) ? id.substring(7) : 0;
_saveState( chapter );
//return false;
});
}
};
return { init : init };
})();
Page.init();
})(window);

Userscript works in Tampermonkey on Chrome but not in Greasemonkey on Firefox

I have been using a userscript in Greasemonkey on Firefox and Tampermonkey on Chrome for over a year and a half now. It adds a scroll to top button on every webpage. I tried adding another site to include in Greasemonkey but then it seemed to have broken it. Now, the scroll to top button doesn't show up on any site in Firefox but the exact same userscript has no issues working in Chrome on Tampermonkey. I did not create this script. I got it from Userscripts.org back before it went down. Below is the script. Does anyone know what might need to be changed in the script to make it compatible with Greasemonkey again?
I'm using Firefox 41.0 for xUbuntu and Greasemonkey 3.4.1.
My Chrome is 45.0.2454.101 and Tampermonkey 3.11.
// ==UserScript==
// #name Scroll To Top Button
// #version v1.3.2
// #include http://*
// #include https://*
// ==/UserScript==
(function(global) {
if(global !== window) return;
function _(id) {
return document.getElementById(id);
}
function bind(context, name) {
return function() {
return context[name].apply(context, arguments);
}
}
global.addEventListener('scroll', scrollHandler, false);
function scrollHandler() {
!scroll.isScrolling && ((scroll.getScrollY() > 0) ? scroll.showBtn() : scroll.hideBtn());
}
var scroll = {
__scrollY : 0,
isScrolling : false, //is scrolling
imgBtn : null,
isBtnShow : false,
pageHeight : 0,
speed : 0.75,
init : function() {
var document = global.document,
div = document.createElement('div'),
css;
css = '#__scrollToTop{font:12px/1em Arial,Helvetica,sans-serif;margin:0;padding:0;position:fixed;display:none;left:92%;top:80%;text-align:center;z-index:999999; width:74px;height:50px;' +
'cursor:pointer;opacity:0.5;padding:2px;}' +
'#__scrollToTop:hover{opacity:1;}' +
'#__scrollToTop span.__scroll__arrow{ position:relative;top:20px;background:none repeat scroll 0 0 #eee;border-style:solid; border-width:1px;' +
'border-color:#ccc #ccc #aaa; border-radius:5px;color:#333;font-size:36px;padding:5px 8px 2px;}' +
' #__scroll__scroll{height:50px;width:50px;float:left;z-index:100001;position:absolute;} ' +
'#__scroll__util{font:12px/1em Arial,Helvetica,sans-serif;text-align:center;height:44px;width:20px;float:right;position:absolute;left:54px;z-index:100000; ' +
'border-style:solid; border-width:1px;border-color:#ccc #ccc #aaa; border-radius:2px;top:5px;display:none;}' +
'#__scroll__util span{display:block;height:18px;padding-top:4px;text-align:center;text-shadow:2px 2px 2px #888;font-size:16px;} ' +
'#__scroll__util span:hover{background-color: #fc9822;}';
GM_addStyle(css);
div.id = '__scrollToTop';
div.title = 'Back To Top';
div.innerHTML = '<div id="__scroll__scroll">' +
'<span class="__scroll__arrow">▲</span>' +
'</div>' +
'<div id="__scroll__util">' +
'<span name="__hide" title="Hide the Button">x</span>' +
'<span name="__bottom" title="Scroll to the bottom">▼</span>' +
'</div>';
document.body.appendChild(div);
div.addEventListener('mousedown', bind(this, 'control'),false);
div.addEventListener('mouseover', bind(this, 'showUtil'),false);
div.addEventListener('mouseout', bind(this, 'hideUtil'),false);
this.util = _('__scroll__util');
this.pageUtil = _('__scroll__page');
this.pageHeight = document.body.scrollHeight;
return this.imgBtn = div;
},
getImgBtn : function() {
return this.imgBtn || this.init();
},
show : function(elem) {
elem.style.display = 'block';
},
hide : function(elem) {
elem.style.display = 'none';
},
showBtn : function() {
if(this.isBtnShow) return;
this.isBtnShow = true;
this.show(this.getImgBtn());
},
hideBtn : function() {
if(!this.isBtnShow) return;
this.isBtnShow = false;
this.hide(this.getImgBtn());
},
getScrollY : function() {
//this piece of code is from John Resig's book 'Pro JavaScript Techniques'
var de = document.documentElement;
return this.__scrollY = (self.pageYOffset ||
( de && de.scrollTop ) ||
document.body.scrollTop);
},
closeBtn : function(event) {
event.preventDefault();
event.stopPropagation();
this.hideBtn();
window.removeEventListener('scroll', scrollHandler, false);
},
showUtil : function() {
this.show(this.util);
},
hideUtil : function() {
this.hide(this.util);
},
scroll : function() {
if(!this.isScrolling) {
this.isScrolling = true;
}
var isStop = false,
scrollY = this.__scrollY;
if(this.direction === 'top') {
isStop = scrollY > 0;
this.__scrollY = Math.floor(scrollY * this.speed);
} else {
isStop = scrollY < this.pageHeight;
this.__scrollY += Math.ceil((this.pageHeight - scrollY) * (1 - this.speed)) + 10;
}
this.isScrolling = isStop;
window.scrollTo(0, this.__scrollY);
isStop ? setTimeout(bind(scroll, 'scroll'), 20) : (this.direction === 'top' && this.hideBtn());
},
control : function(e) {
var t = e.target, name = t.getAttribute('name');
switch(name) {
case '__bottom':
this.scrollToBottom();
break;
case '__hide' :
this.closeBtn(e);
break;
default :
this.scrollToTop();
break;
}
},
scrollToTop : function() {
this.direction = 'top';
this.scroll();
},
scrollToBottom : function() {
this.direction = 'bottom';
var bodyHeight = global.document.body.scrollHeight,
documentElementHeight = global.document.documentElement.scrollHeight;
this.pageHeight = Math.max(bodyHeight, documentElementHeight);
this.scroll();
}
};
//Autoscroll
(function() {
var isAutoScroll = false;
var autoScroll = {
__autoScrollID : 0,
isAutoScroll : false,
defaultSpeed : 1,
currentSpeed : 1,
intervalTime : 100,
reset : function() {
this.isAutoScroll && (this.currentSpeed = this.defaultSpeed);
},
startOrStop : function() {
var that = this;
if(that.isAutoScroll) {
that.isAutoScroll = false;
clearInterval(that.__autoScrollID);
} else {
that.isAutoScroll = true;
that.__autoScrollID = setInterval(function() {
global.scrollBy(0, that.currentSpeed);
}, that.intervalTime);
}
},
fast : function() {
this.isAutoScroll && this.currentSpeed <= 10 && this.currentSpeed++;
},
slow : function() {
this.isAutoScroll && this.currentSpeed > 1 && this.currentSpeed--;
},
keyControl : function(e) {
if(e.target != global.document.body && e.target != global.document.documentElement) return false; // only when the cursor focus on the page rather than the input area can trigger this event.
var charCode = e.charCode,
key = this.keyMap[charCode];
key && this[key]();
},
keyMap : {
'100' : 'slow', // press 'd', slow the speed of the scroll
'102' : 'fast', // press 'f', speed scroll
'114' : 'reset', // press 'r', reset the autoscroll's speed
'115' : 'startOrStop' //when you click 's' at the first time, the autoscroll is begin, and then you click again, it will stop.
}
};
global.addEventListener('keypress', bind(autoScroll, 'keyControl'), false);
}())
}(window.top))
Add before // ==/UserScript== on a new line:
// #grant GM_addStyle
Actually I'm surprised it did work until now because Greasemonkey requires #grant for over a year.

Draggable code not working with hammer.js

I've successfully implemented jQueryUI draggable, but as soon as I add hammer.js code, the draggable code no longer works.
It is not as soon as I include hammer.js, but as soon as I use the script.
Why is this? How can I get them both to work?
Both the draggable and hammer are applied to .dataCard and #main
The draggable code works fine here ( with hammer implementation commented out ): http://goo.gl/MO5Pde
Here is an example of the draggable code:
$('#main').draggable({
axis:'y',
revert:true,
start: function(event, ui){
topValue = ui.position.top;
},
drag: function(event, ui){
if(pastBreakpoint === false){
$('#searchInput').blur();
if(topValue > ui.position.top) return false;
if(ui.position.top >= 161){
if(pastBreakpoint === false){
pastBreakpoint = true;
if($('.loadingRefresh').length === 0) $('#main').before('<div class="loadingRefresh"></div>');
else{
$('.loadingRefresh').remove();
$('#main').before('<div class="loadingRefresh"></div>');
}
$('.loadingRefresh').fadeIn();
$('#main').mouseup();
setTimeout(function(){
location.reload();
}, 1000);
}
}
}
}
});
Here is the hammer code uncommented and the draggable code not working: http://goo.gl/994pxF
Here is the hammer code:
var hammertime = Hammer(document.getElementById('main'), {
transform_always_block: true,
transform_min_scale: 0
});
var posX = 0,
posY = 0,
lastPosX = 0,
lastPosY = 0,
bufferX = 0,
bufferY = 0,
scale = 1,
last_scale = 1;
hammertime.on('touch transform transformend', function(ev) {
if ((" " + ev.target.className + " ").indexOf(" dataCard ") < 0) return;
else manageMultitouch(ev, ev.target); });
function manageMultitouch(ev, element) {
switch (ev.type) {
case 'touch':
last_scale = scale;
return;
case 'transform':
scale = Math.min(last_scale * ev.gesture.scale, 10);
break;
}
if(scale <= 0.5) $(element).hide('clip');
if(scale > 1.0) $(element).addClass('focused');
var transform = "translate(" + 0 + "px," + 0 + "px) " + "scale(" + 1 + "," + scale + ")";
var style = element.style;
style.transform = transform;
style.oTransform = transform;
style.msTransform = transform;
style.mozTransform = transform;
style.webkitTransform = transform;
}
I had the same problem in my app, even with touch punch included. I had to do a good research to find what was the problem stopping the jquery ui drag.
The problem occurring is a preventDefault set at the event ( only when hammer is included ) changing the result of a trigger method from jquery ui.
Well, lets get back a little bit:
The first method you should see is the _mouseMove(), which is connected with the mousemove event.
The drag will be trigged only when the condition (this._mouseStart(this._mouseDownEvent, event) !== false) be true.
_mouseMove: function (event) {
// IE mouseup check - mouseup happened when mouse was out of window
if ($.ui.ie && (!document.documentMode || document.documentMode < 9) && !event.button) {
return this._mouseUp(event);
}
if (this._mouseStarted) {
this._mouseDrag(event);
return event.preventDefault();
}
if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
this._mouseStarted =
(this._mouseStart(this._mouseDownEvent, event) !== false);
(this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
}
return !this._mouseStarted;
}
The next method will create the helper ( element's clone ), set some css in the element and return true ( value we expect ), unless this._trigger("start", event) returns false.
_mouseStart: function(event) {
var o = this.options;
//Create and append the visible helper
this.helper = this._createHelper(event);
this.helper.addClass("ui-draggable-dragging");
//Cache the helper size
this._cacheHelperProportions();
//If ddmanager is used for droppables, set the global draggable
if($.ui.ddmanager) {
$.ui.ddmanager.current = this;
}
/*
* - Position generation -
* This block generates everything position related - it's the core of draggables.
*/
//Cache the margins of the original element
this._cacheMargins();
//Store the helper's css position
this.cssPosition = this.helper.css( "position" );
this.scrollParent = this.helper.scrollParent();
this.offsetParent = this.helper.offsetParent();
this.offsetParentCssPosition = this.offsetParent.css( "position" );
//The element's absolute position on the page minus margins
this.offset = this.positionAbs = this.element.offset();
this.offset = {
top: this.offset.top - this.margins.top,
left: this.offset.left - this.margins.left
};
//Reset scroll cache
this.offset.scroll = false;
$.extend(this.offset, {
click: { //Where the click happened, relative to the element
left: event.pageX - this.offset.left,
top: event.pageY - this.offset.top
},
parent: this._getParentOffset(),
relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
});
//Generate the original position
this.originalPosition = this.position = this._generatePosition(event);
this.originalPageX = event.pageX;
this.originalPageY = event.pageY;
//Adjust the mouse offset relative to the helper if "cursorAt" is supplied
(o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
//Set a containment if given in the options
this._setContainment();
//Trigger event + callbacks
if(this._trigger("start", event) === false) {
this._clear();
return false;
}
//Recache the helper size
this._cacheHelperProportions();
//Prepare the droppable offsets
if ($.ui.ddmanager && !o.dropBehaviour) {
$.ui.ddmanager.prepareOffsets(this, event);
}
this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
//If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
if ( $.ui.ddmanager ) {
$.ui.ddmanager.dragStart(this, event);
}
return true;
}
Below is the first _trigger called, its from drag widget.
_trigger: function (type, event, ui) {
ui = ui || this._uiHash();
$.ui.plugin.call(this, type, [event, ui]);
//The absolute position has to be recalculated after plugins
if(type === "drag") {
this.positionAbs = this._convertPositionTo("absolute");
}
return $.Widget.prototype._trigger.call(this, type, event, ui);
}
At this point the result will call another trigger method (this time from the $.Widget) and that's the point where we have the problem.
_trigger: function (type, event, data) {
var prop, orig,
callback = this.options[type];
data = data || {};
event = $.Event(event);
event.type = (type === this.widgetEventPrefix ?
type :
this.widgetEventPrefix + type).toLowerCase();
// the original event may come from any element
// so we need to reset the target on the new event
event.target = this.element[0];
// copy original event properties over to the new event
orig = event.originalEvent;
if (orig) {
for (prop in orig) {
if (!(prop in event)) {
event[prop] = orig[prop];
}
}
}
return !($.isFunction(callback) && callback.apply(this.element[0], [event].concat(data)) === false || event.isDefaultPrevented());
}
return !($.isFunction(callback) && callback.apply(this.element[0], [event].concat(data)) === false || event.isDefaultPrevented());
Our problem is exactly at this line. More specific the || before event.isDefaultPrevented().
When hammer is included the method event.isDefaultPrevented() is resulting true, once the value is denied before return, the final value would be false.
(Without the hammer included the event.isDefaultPrevented() returns false as expected.)
Backing in our _moseMouve(), instead of calling the _mouseDrag() method it'll invoke _mouseUp().
U can see it will unbind the events and call _mouseStop().
_mouseUp: function (event) {
$(document)
.unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
.unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
if (this._mouseStarted) {
this._mouseStarted = false;
if (event.target === this._mouseDownEvent.target) {
$.data(event.target, this.widgetName + ".preventClickEvent", true);
}
this._mouseStop(event);
}
return false;
}
If you change the OR (||) operator by an AND (&&) it'll work fine. Off course it's not a little change, I had been testing it and until this moment I haven't find any problem at all. The line would be like this:
return !($.isFunction(callback) && callback.apply(this.element[0], [event].concat(data)) === false && event.isDefaultPrevented());
As I said, its not 100% secure, but until now I didn't find a reason to keep || instead of &&. I'll keep testing it for a few days.
Besides I've already sent an email to the lead developer from jquery ui asking about it.
Similar problem for me using it with isomorphic smartclient.
I fixed it by handling the start event and resetting isDefaultPrevented to false
$(element).draggable({
start: function (event, ui) {
event.isDefaultPrevented = function () { return false; }
}
});
I had the same problem and found this issue at github.
the shown solution using event delegation worked fine for me:
$(document).hammer().on('touch transform transformend', '#main', function() {...});

Jquery slideshow starts with an image on the left of a row but I want it to start at the right end

I'm using this javascript and the slide show slides right to left with the images in this order and positon:
start postion > 1 | 2 | 3 | 4 | 5 | 6 etc etc
but I want to swap them so they run in this position
6 | 5 | 4 | 3 | 2 | 1 < start position
Kind of like reading a book back to front, but keeping it in the right order
I've been told I need to modify the lines labelled below: //MODIFY ME
I hope someone can help! Thank you
Here's my code
(function($) {
$.fn.slideshow = function(method) {
if ( this[0][method] ) {
return this[0][ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return this.each(function() {
var ANIMATION_DURATION = .6; // The duration to flick the content. In seconds.
var MOVE_THRESHOLD = 10; // Since touch points can move slightly when initiating a click this is the
// amount to move before allowing the element to dispatch a click event.
var itemWidth;
var horizontalGap;
var $this = $(this);
var collection;
var viewItems = [];
var touchStartTransformX; // The start transformX when the user taps.
var touchStartX; // The start x coord when the user taps.
var interval; // Interval used for measuring the drag speed.
var wasContentDragged; // Flag for whether or not the content was dragged. Takes into account MOVE_THRESHOLD.
var targetTransformX; // The target transform X when a user flicks the content.
var touchDragCoords = []; // Used to keep track of the touch coordinates when dragging to measure speed.
var touchstartTarget; // The element which triggered the touchstart.
var selectedIndex = 0; // The current visible page.
var viewPortWidth; // The width of the div that holds the horizontal content.
var isAnimating;
var pageChangedLeft;
// The x coord when the items are reset.
var resetX;
var delayTimeout;
init(method);
function init(options) {
collection = options.data;
renderer = options.renderer;
itemWidth = options.itemWidth;
horizontalGap = options.horizontalGap;
initLayout();
$this[0].addEventListener("touchstart", touchstartHandler);
$this[0].addEventListener("mousedown", touchstartHandler);
viewPortWidth = $this.width();
$this.on("webkitTransitionEnd", transitionEndHandler);
collection.on("add", addItem);
}
// MODIFY ME
function initLayout() {
// Layout five items. The one in the middle is always the selected one.
for (var i = 0; i < 5; i++) {
var viewItem;
if (i > 1 && collection.at(i - 2)) // Start at the one in the middle. Subtract 2 so data index starts at 0.
viewItem = new renderer({model: collection.at(i - 2)});
else
viewItem = new renderer();
viewItem.render().$el.appendTo($this);
viewItem.$el.css("left", itemWidth * i + horizontalGap * i);
viewItem.setState(i != 2 ? "off" : "on");
viewItems.push(viewItem);
}
// Center the first viewItem
resetX = itemWidth * 2 - ($this.width() - itemWidth - horizontalGap * 4) / 2;
setTransformX(-resetX);
}
function getCssLeft($el) {
var left = $el.css("left");
return Number(left.split("px")[0]);
}
// MODIFY ME
function transitionEndHandler() {
if (pageChangedLeft != undefined) {
var viewItem;
if (pageChangedLeft) {
// Move the first item to the end.
viewItem = viewItems.shift();
viewItems.push(viewItem);
viewItem.model = collection.at(selectedIndex + 2);
viewItem.$el.css("left", getCssLeft(viewItems[3].$el) + itemWidth + horizontalGap);
} else {
// Move the last item to the beginning.
viewItem = viewItems.pop();
viewItems.splice(0, 0, viewItem);
viewItem.model = collection.at(selectedIndex - 2);
viewItem.$el.css("left", getCssLeft(viewItems[1].$el) - itemWidth - horizontalGap);
}
viewItem.render();
// Reset the layout of the items.
for (var i = 0; i < 5; i++) {
var viewItem = viewItems[i];
viewItem.$el.css("left", itemWidth * i + horizontalGap * i);
viewItem.setState(i != 2 ? "off" : "on");
}
// Reset the transformX so we don't run into any rendering limits. Can't find a definitive answer for what the limits are.
$this.css("-webkit-transition", "none");
setTransformX(-resetX);
pageChangedLeft = undefined;
}
}
function touchstartHandler(e) {
clearInterval(interval);
wasContentDragged = false;
transitionEndHandler();
// Prevent the default so the window doesn't scroll and links don't open immediately.
e.preventDefault();
// Get a reference to the element which triggered the touchstart.
touchstartTarget = e.target;
// Check for device. If not then testing on desktop.
touchStartX = window.Touch ? e.touches[0].clientX : e.clientX;
// Get the current transformX before the transition is removed.
touchStartTransformX = getTransformX();
// Set the transformX before the animation is stopped otherwise the animation will go to the end coord
// instead of stopping at its current location which is where the drag should begin from.
setTransformX(touchStartTransformX);
// Remove the transition so the content doesn't tween to the spot being dragged. This also moves the animation to the end.
$this.css("-webkit-transition", "none");
// Create an interval to monitor how fast the user is dragging.
interval = setInterval(measureDragSpeed, 20);
document.addEventListener("touchmove", touchmoveHandler);
document.addEventListener("touchend", touchendHandler);
document.addEventListener("mousemove", touchmoveHandler);
document.addEventListener("mouseup", touchendHandler);
}
function measureDragSpeed() {
touchDragCoords.push(getTransformX());
}
function touchmoveHandler(e) {
var deltaX = (window.Touch ? e.touches[0].clientX : e.clientX) - touchStartX;
if (wasContentDragged || Math.abs(deltaX) > MOVE_THRESHOLD) { // Keep track of whether or not the user dragged.
wasContentDragged = true;
setTransformX(touchStartTransformX + deltaX);
}
}
function touchendHandler(e) {
document.removeEventListener("touchmove", touchmoveHandler);
document.removeEventListener("touchend", touchendHandler);
document.removeEventListener("mousemove", touchmoveHandler);
document.removeEventListener("mouseup", touchendHandler);
clearInterval(interval);
e.preventDefault();
if (wasContentDragged) { // User dragged more than MOVE_THRESHOLD so transition the content.
var previousX = getTransformX();
var bSwitchPages;
// Compare the last 5 coordinates
for (var i = touchDragCoords.length - 1; i > Math.max(touchDragCoords.length - 5, 0); i--) {
if (touchDragCoords[i] != previousX) {
bSwitchPages = true;
break;
}
}
// User dragged more than halfway across the screen.
if (!bSwitchPages && Math.abs(touchStartTransformX - getTransformX()) > (viewPortWidth / 2))
bSwitchPages = true;
if (bSwitchPages) {
if (previousX > touchStartTransformX) { // User dragged to the right. go to previous page.
if (selectedIndex > 0) { // Make sure user is not on the first page otherwise stay on the same page.
selectedIndex--;
tweenTo(touchStartTransformX + itemWidth + horizontalGap);
pageChangedLeft = false;
} else {
tweenTo(touchStartTransformX);
pageChangedLeft = undefined;
}
} else { // User dragged to the left. go to next page.
if (selectedIndex + 1 < collection.length) {// Make sure user is not on the last page otherwise stay on the same page.
selectedIndex++;
tweenTo(touchStartTransformX - itemWidth - horizontalGap);
pageChangedLeft = true;
} else {
tweenTo(touchStartTransformX);
pageChangedLeft = undefined;
}
}
} else {
tweenTo(touchStartTransformX);
pageChangedLeft = undefined;
}
} else { // User dragged less than MOVE_THRESHOLD trigger a click event.
var event = document.createEvent("MouseEvents");
event.initEvent("click", true, true);
touchstartTarget.dispatchEvent(event);
}
}
// Returns the x of the transform matrix.
function getTransformX() {
var transformArray = $this.css("-webkit-transform").split(","); // matrix(1, 0, 0, 1, 0, 0)
var transformElement = $.trim(transformArray[4]); // remove the leading whitespace.
return transformX = Number(transformElement); // Remove the ).
}
// Sets the x of the transform matrix.
function setTransformX(value) {
$this.css("-webkit-transform", "translateX("+ Math.round(value) + "px)");
}
function tweenTo(value) {
isAnimating = true;
targetTransformX = value;
// Set the style for the transition.
$this.css("-webkit-transition", "-webkit-transform " + ANIMATION_DURATION + "s");
// Need to set the timing function each time -webkit-transition is set.
// The transition is set to ease-out.
$this.css("-webkit-transition-timing-function", "cubic-bezier(0, 0, 0, 1)");
setTransformX(targetTransformX);
}
// MODIFY ME
function addItem(folio) {
clearTimeout(delayTimeout);
// Create a timeout in case multiple items are added in the same frame.
// When the timeout completes all of the view items will have their model
// updated. The renderer should check to make sure the model is different
// before making any changes.
delayTimeout = setTimeout(function(folio) {
var index = collection.models.indexOf(folio);
var dataIndex = index;
var firstIndex = selectedIndex - 2;
var dataIndex = firstIndex;
var viewItem;
for (var i = 0; i < viewItems.length; i++) {
viewItem = viewItems[i];
if (dataIndex >= 0 && dataIndex < collection.length) {
viewItem.model = collection.at(dataIndex);
viewItem.render();
}
viewItem.setState(i != 2 ? "off" : "on");
dataIndex += 1;
}
}, 200);
}
// Called when the data source has changed. Resets the view with the new data source.
this.setData = function(data) {
$this.empty();
viewItems = [];
collection = data;
selectedIndex = 0;
initLayout();
}
});
} else {
$.error( 'Method ' + method + ' does not exist on Slideshow' );
}
}
})(jQuery);
From what I can make out, you need to simply "flip" the loops that create the sides in the slideshow so that it makes the last slide where it was making the first. It seems to do this in two places.
Then, you will need to amend the code which adds a slide to make it add it before the other slides instead of after.
This sounds an awful lot like homework - it's always best to attempt an answer before asking on here. An example on a site like JSFiddle is also generally appreciated.

Categories