photoswipe returning to initial hash of current page instead of last scroll position - url

On IOS, when I close photoswipe to return to the page, it wont return to the scroll position I was at when I clicked the thumbnail.
Instead the page scrolls back to the # which was specified when I initially called the page.
For example if photoswipe is on www.somepage.html, and I navigate to the page using:
www.somepage.html#footer
and then scroll up and click a thumnail in #middle of page, on closing photoswipe, the page scrolls back down to the footer.
I've tried disabling history in the photswipe options, and i've also tried clearing the hash data from the url using:
//clear hash
//$(document).ready(function (e) {
// window.location.hash = '';
// window.history.pushState("", document.title, window.location.pathname);
//
//});
But none of it seems to work. If I navigate to the page without the # in the page, everthing is fine.
I'm guessing I may have to pass a variable in the url instead of the # and scroll to the div in question via javascript?
I already have the javascript in place to scroll, but I'm not sure how to read the variable from the url and then use it's value in Javascript.
If this is likely to be the best fix for the issue, could anyone give an example of the javascript code needed?
Here's my current scroll code:
$(function () {
$('a[href*=#]:not([href=#],[data-toggle],[data-target],[data-slide])').click(function () {
if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') || location.hostname == this.hostname) {
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
if (target.length) {
$('html,body').animate({
scrollTop: target.offset().top
}, 1000);
return false;
}
}
});
});

If anyone else has the same issue, I've managed to fix this by passing the div id to the page in the query string rather than using a #.
Here's the code:
$(window).ready(function () {
if (document.location.search.length) {
target = getUrlVars()["id"];
scrollToID('#' + target, 750);
} else {
return;
}
//target = $url().param('id');
//if (target == '') return;
});
function getUrlVars() {
var vars = [], hash;
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for (var i = 0; i < hashes.length; i++) {
hash = hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
}
// scroll function
function scrollToID(id, speed){
var offSet = 100;
var targetOffset = $(id).offset().top - offSet;
var mainNav = $('#main-nav');
$('html,body').animate({scrollTop:targetOffset}, speed);
if (mainNav.hasClass("open")) {
mainNav.css("height", "1px").removeClass("in").addClass("collapse");
mainNav.removeClass("open");
}
}
if (typeof console === "undefined") {
console = {
log: function() { }
};
}

Related

$(window).scroll(function () {}) is not working

I'm trying to perform some action on window scroll event but it is not working.
Here is my code
$(window).scroll(function () {
// var limit = 7; //The number of records to display per request
var lastID = $newsfeed_start;
if (($(window).scrollTop() == $(document).height() - $(window).height()&& (lastID != 0)) {
// increment strat value
$newsfeed_start = $newsfeed_start + $newsfeed_limit;
get_timeline_post('');
}
});
even $(window).scroll(function () { } ) function is not working
Your CSS is actually setting the rest of the document to not show overflow therefore the document itself isn't scrolling. The easiest fix for this is bind the event to the thing that is scrolling, which in your case is div#page.
So its easy as changing:
$(document).scroll(function() { // OR $(window).scroll(function() {
didScroll = true;
});
to
$('div#page').scroll(function() {
didScroll = true;
});

ValidationSummary hyperlink doesn't change tab

Im using devexpress Html.DevExpress().ValidationSummary in my mvc project. My form contains a devexpress page control with 3 tabs. When there are client-side validation errors validation summary shows them, and every error is hyperlink. If you click it, the field with error is focused, but if the field is on other tab nothing happens. So the field is focused if it is on active tab. I'd like it to change the active tab and focus appropriate field. Is there a way to make it work right?
My colleague solved this problem using javascript and jQuery:
var nameControlTab = "myPageControl"; //name of your pagecontrol here
$(document).ready(function () {
$("tbody").on('click', 'a[href*="javascript:_aspxVSOnErrorClick("]', function () {
var url = $(this).attr("href");
url = url.substring(url.indexOf("'") + 1);
url = url.substring(0, url.indexOf("'"));
var idTab = $("#" + url + "_ET").parents('div[id*="' + nameControlTab + '_C"]').not(nameControlTab + '_CC')[0].id;
var indexTab = idTab.substring((nameControlTab + '_C').length);
SetTab(indexTab);
return true;
});
});
function indexOf(array, value) {
if ([].indexOf)
return array.indexOf(value);
else {
for (var i = 0; i < array.length; i++)
if (array[i] == value)
return i;
return -1;
}
}
function SetTab(indexTab) {
this[nameControlTab].SetActiveTabIndex(indexTab);
}

Hashchange causing phonegap app to crash?

I'm using ajax within my phonegap app to load the content for different pages. I'm also using the onhashchange to enable back button functionality.
All works fine initially but if I click 3 different links (and trigger 3 hash changes) then my app completely crashes, becomes unresponsive and then eats up all my memory.
The code of the hash change is below, anyone got any ideas why it might be crashing/memory leaking?
$('a.ajax').click(function () {
location.hash = $(this).attr('href').match(/(^.*)\./)[1]
return false
})
function hashChange() {
var page = location.hash.slice(1)
if (page != "" && window.location.hash) {
wrap.load('pages/' + page + ".html .page-wrapper", function(){
closeMenu();
})
}else{
wrap.load('pages/Welcome.html .page-wrapper', function(){
closeMenu();
})
}
}
// check for hash change
if ("onhashchange" in window) {
$(window).on('hashchange', hashChange).trigger('hashchange')
} else { // lame browser
var lastHash = ''
setInterval(function () {
if (lastHash != location.hash)
hashChange()
lastHash = location.hash
//contentScroller.scrollTo(0,0);
}, 100)
}
Just to note, I'm getting the following error within the xCode console and I'm running version 2.9 of phonegap
CDVWebViewDelegate: Navigation started when state=1
Thanks!
I wasn't entirely sure what was causing the crash but I decided to use this plugin http://benalman.com/projects/jquery-hashchange-plugin/ and now everything works fine
var wrap = $('#contentScroller .scroller');
$('a.ajax').click(function () {
location.hash = $(this).attr('href').match(/(^.*)\./)[1];
return false;
});
$(window).hashchange(function () {
var page = location.hash.slice(1);
if (page !== "" && window.location.hash) {
wrap.load('pages/' + page + ".html .page-wrapper", function () {
closeMenu();
});
} else {
wrap.load('pages/Welcome.html .page-wrapper', function () {
closeMenu();
});
}
});

window.opener not set in iOS Chrome

In one file, I have
go
In t2.html I have
<script>
document.write(window.opener);
</script>
On Safari on iOS, and on Chrome on the Mac and on pretty much every other browser, it prints out [object Window] like you'd expect.
On Chrome on iOS, I get null.
How do I get to the window that opened this window?
This code solves the problem you are talking about (specifically for issues with Chrome ios not liking "pop ups"), but in reference to Paypal Adaptive Payments where it opens a "pop up" and redirects to Paypal page for payment.
The key is that you have to:
Initiate the window.open directly from a button/link click
You must use the _blank as the window "name" (and not choose your own)
The main thing you want/need is:
var win;
//VERY IMPORTANT - You must use '_blank' and NOT name the window if you want it to work with chrome ios on iphone
//See this bug report from google explaining the issue: https://code.google.com/p/chromium/issues/detail?id=136610
win = window.open(paypalURL,'_blank');
//Initiate returnFromPayPal function if the pop up window is closed
if (win && win.closed) {
returnFromPayPal();
}
Here is the full code that you can follow (ignore anything that doesn't apply to what you are doing).
<div>
<?php $payUrl = 'https://www.paypal.com/webapps/adaptivepayment/flow/pay?expType=mini&paykey=' . $payKey ?>
<button onclick="loadPayPalPage('<?php echo $payUrl; ?>')" title="Pay online with PayPal">PayPal</button>
</div>
<script>
function loadPayPalPage(paypalURL)
{
var ua = navigator.userAgent;
var pollingInterval = 0;
var win;
// mobile device
if (ua.match(/iPhone|iPod|Android|Blackberry.*WebKit/i)) {
//VERY IMPORTANT - You must use '_blank' and NOT name the window if you want it to work with chrome ios on iphone
//See this bug report from google explaining the issue: https://code.google.com/p/chromium/issues/detail?id=136610
win = window.open(paypalURL,'_blank');
pollingInterval = setInterval(function() {
if (win && win.closed) {
clearInterval(pollingInterval);
returnFromPayPal();
}
} , 1000);
}
else
{
//Desktop device
var width = 400,
height = 550,
left,
top;
if (window.outerWidth) {
left = Math.round((window.outerWidth - width) / 2) + window.screenX;
top = Math.round((window.outerHeight - height) / 2) + window.screenY;
} else if (window.screen.width) {
left = Math.round((window.screen.width - width) / 2);
top = Math.round((window.screen.height - height) / 2);
}
//VERY IMPORTANT - You must use '_blank' and NOT name the window if you want it to work with chrome ios on iphone
//See this bug report from google explaining the issue: https://code.google.com/p/chromium/issues/detail?id=136610
win = window.open(paypalURL,'_blank','top=' + top + ', left=' + left + ', width=' + width + ', height=' + height + ', location=0, status=0, toolbar=0, menubar=0, resizable=0, scrollbars=1');
pollingInterval = setInterval(function() {
if (win && win.closed) {
clearInterval(pollingInterval);
returnFromPayPal();
}
} , 1000);
}
}
var returnFromPayPal = function()
{
location.replace("www.yourdomain.com/paypalStatusCheck.php");
// Here you would need to pass on the payKey to your server side handle (use session variable) to call the PaymentDetails API to make sure Payment has been successful
// based on the payment status- redirect to your success or cancel/failed page
}
</script>
This seems to be a bigger story. See Bugtracker here:
http://code.google.com/p/chromium/issues/detail?id=136610&q=window.opener&colspec=ID%20Pri%20Mstone%20ReleaseBlock%20OS%20Area%20Feature%20Status%20Owner%20Summary
But it seems, as if iframes could handle the parent-property, so maybe you could switch your app from using popups to using an overlay.
If you want to pass values from child to parent use the following code.
Add following code to parent page:
var hidden, state, visibilityChange;
if (typeof document.hidden !== "undefined") {
hidden = "hidden";
visibilityChange = "visibilitychange";
state = "visibilityState";
} else if (typeof document.mozHidden !== "undefined") {
hidden = "mozHidden";
visibilityChange = "mozvisibilitychange";
state = "mozVisibilityState";
} else if (typeof document.msHidden !== "undefined") {
hidden = "msHidden";
visibilityChange = "msvisibilitychange";
state = "msVisibilityState";
} else if (typeof document.webkitHidden !== "undefined") {
hidden = "webkitHidden";
visibilityChange = "webkitvisibilitychange";
state = "webkitVisibilityState";
}
// Add a listener that constantly changes the title
document.addEventListener(visibilityChange, function () {
if (localStorage.getItem("AccountName")) {
$("#txtGrower").val(localStorage.getItem("AccountName"));
}
if (localStorage.getItem("AccountID")) {
$("#hdnGrower").val(localStorage.getItem("AccountID"));
}
}, false);
Add following in child page (Any preferred event)
function CloseChildAndLoadValuesToParent() {
localStorage.setItem("AccountName", 'MyAccountName');
localStorage.setItem("AccountID", 'MyAccountID');
window.close();
}

Latency issue with Primefaces overlayPanel - loads to lazy

I am using Primefaces 3.2 with jsf 2 and glassfish 3.1.2.
I have a p:dataTable of users containing avatars of the user. Whenever the user moves the mouse over the avatar a p:overlayPanel appears with more information (lazy loaded) on the user, and disappears when the user moves the cursor away - like this:
<p:overlayPanel for="avatar" dynamic="true" showEvent="mouseover" hideEvent="mouseout" ...>
This works very well - as long as the user is "slowhanded". Whenever an user moves the cursor fast above many avatars many of the overlayPanels stay visible.
For example when the user has the cursor over the position where user avatars are displayed and uses the scroll wheel of his mouse to scroll the usertable down or up.
I believe that the overlaypanel starts to load the information dynamically (dynamic="true") from the server when showEvent="mouseover" is dispatched and displays the overlaypanel after the response from the server arrives.
This way it is not possible to detect whether the cursor is already away when the overlaypanel becomes visible - so the hideEvent="mouseout" is never dispatched.
Is there a way to make the primefaces overlaypanel appear directly on mousover, showing a loading gif and update the content into the overlaypanel when the response comes from the server.
Is this a good appraoch or does anyone know any other way to solve this nasty problem?
Thanks Pete
As my first answer is already very long and contains valid information, I decided to open a new answer presenting my final approach.
Im now using Primefaces inheritance pattern making the code alot cleaner. Also I noticed that replacing/overwriting the whole bindEvents function isnt necessary, as we can remove the old event handlers. Finally this code fixs the latest issue experienced: A hide event before ajax arrival.
PrimeFaces.widget.OverlayPanel = PrimeFaces.widget.OverlayPanel
.extend({
bindEvents : function() {
this._super();
var showEvent = this.cfg.showEvent + '.ui-overlay', hideEvent = this.cfg.hideEvent
+ '.ui-overlay';
$(document).off(showEvent + ' ' + hideEvent, this.targetId).on(
showEvent, this.targetId, this, function(e) {
var _self = e.data;
clearTimeout(_self.timer);
_self.timer = setTimeout(function() {
_self.hidden = false;
_self.show();
}, 300);
}).on(hideEvent, this.targetId, this, function(e) {
var _self = e.data;
clearTimeout(_self.timer);
_self.hidden = true;
_self.hide();
});
},
_show : function() {
if (!this.cfg.dynamic || !this.hidden) {
this._super();
}
}
});
Im sorry for the poor formatting: Eclipses fault ;)
Wow, finally after a long debuging session and testing various approaches i recognized that the problem isnt the ajax request but the event handlers itself:
.on(hideEvent, this.targetId, this, function(e) {
var _self = e.data;
if(_self.isVisible()) {
_self.hide();
}
});
As you can see, the widget is just hidden if its visible before. If your moving your mouse out too fast, now two things can happen:
The widget isnt visible at all
The animation is still going on
In this case the event is discarded and the panel stays visible. As animations are queued, one simply has to remove the if statement to fix the issue. I did this by replacing the whole bindEvents method:
PrimeFaces.widget.OverlayPanel.prototype.bindEvents = function() {
//mark target and descandants of target as a trigger for a primefaces overlay
this.target.data('primefaces-overlay-target', this.id).find('*').data('primefaces-overlay-target', this.id);
//show and hide events for target
if(this.cfg.showEvent == this.cfg.hideEvent) {
var event = this.cfg.showEvent;
$(document).off(event, this.targetId).on(event, this.targetId, this, function(e) {
e.data.toggle();
});
}
else {
var showEvent = this.cfg.showEvent + '.ui-overlay',
hideEvent = this.cfg.hideEvent + '.ui-overlay';
$(document).off(showEvent + ' ' + hideEvent, this.targetId).on(showEvent, this.targetId, this, function(e) {
var _self = e.data;
if(!_self.isVisible()) {
_self.show();
}
})
.on(hideEvent, this.targetId, this, function(e) {
var _self = e.data;
_self.hide();
});
}
//enter key support for mousedown event
this.bindKeyEvents();
var _self = this;
//hide overlay when mousedown is at outside of overlay
$(document.body).bind('mousedown.ui-overlay', function (e) {
if(_self.jq.hasClass('ui-overlay-hidden')) {
return;
}
//do nothing on target mousedown
var target = $(e.target);
if(_self.target.is(target)||_self.target.has(target).length > 0) {
return;
}
//hide overlay if mousedown is on outside
var offset = _self.jq.offset();
if(e.pageX < offset.left ||
e.pageX > offset.left + _self.jq.outerWidth() ||
e.pageY < offset.top ||
e.pageY > offset.top + _self.jq.outerHeight()) {
_self.hide();
}
});
//Hide overlay on resize
var resizeNS = 'resize.' + this.id;
$(window).unbind(resizeNS).bind(resizeNS, function() {
if(_self.jq.hasClass('ui-overlay-visible')) {
_self.hide();
}
});
};
Execute this code on load and the issue should be gone.
As your replacing the js code nevertheless, you can use this oppurtunity to implement quite a nice feature. By using timeouts in the event handlers one can easily implement a little delay not just improving usability (no more thousands of popups appear) but also reducing network traffic:
$(document).off(showEvent + ' ' + hideEvent, this.targetId).on(showEvent, this.targetId, this, function(e) {
var _self = e.data;
_self.timer = setTimeout( function(){
if(!_self.isVisible()) {
_self.show();
}
}, 300);
})
.on(hideEvent, this.targetId, this, function(e) {
var _self = e.data;
clearTimeout(_self.timer);
_self.hide();
});
Ofcourse you can use a global variable to control the delay time. If you want a more flexible approach youll have to overwrite the encodeScript method in the OverlayPanelRender to transmit an additional property. You could access it then with _self.cfg.delay. Notice though that youll have to replace the component model OverlayPanel too providing it with an extra attribute.
At the same time I thank you for this brilliant solution I take the opportunity to update it for Primefaces 5.2. In our application the code broke after that upgrade.
Follows the updated code for Primefaces 5.2:
PrimeFaces.widget.OverlayPanel.prototype.bindTargetEvents = function() {
var $this = this;
//mark target and descandants of target as a trigger for a primefaces overlay
this.target.data('primefaces-overlay-target', this.id).find('*').data('primefaces-overlay-target', this.id);
//show and hide events for target
if(this.cfg.showEvent === this.cfg.hideEvent) {
var event = this.cfg.showEvent;
this.target.on(event, function(e) {
$this.toggle();
});
}
else {
var showEvent = this.cfg.showEvent + '.ui-overlaypanel',
hideEvent = this.cfg.hideEvent + '.ui-overlaypanel';
this.target
.off(showEvent + ' ' + hideEvent)
.on(showEvent, function(e) {
clearTimeout($this.timer);
$this.timer = setTimeout(function() {
$('.ui-overlaypanel').hide();
$this.hidden = false;
$this.show();
}, 500);
})
.on(hideEvent, function(e) {
clearTimeout($this.timer);
$this.timer = setTimeout(function() {
// don't hide if hovering overlay
if(! $this.jq.is(":hover")) {
$this.hide();
}
}, 100);
});
}
$this.target.off('keydown.ui-overlaypanel keyup.ui-overlaypanel').on('keydown.ui-overlaypanel', function(e) {
var keyCode = $.ui.keyCode, key = e.which;
if(key === keyCode.ENTER||key === keyCode.NUMPAD_ENTER) {
e.preventDefault();
}
})
.on('keyup.ui-overlaypanel', function(e) {
var keyCode = $.ui.keyCode, key = e.which;
if(key === keyCode.ENTER||key === keyCode.NUMPAD_ENTER) {
$this.toggle();
e.preventDefault();
}
});
};
I also added an extra feature which allows the user to move the mouse over the overlay without hiding it. It should hide when you move the mouse out of it then which I accomplished through:
<p:overlayPanel .... onShow="onShowOverlayPanel(this)" ...>
function onShowOverlayPanel(ovr) {
ovr.jq.on("mouseleave", function(e) {
ovr.jq.hide();
});
}
Hope you enjoy!
It's been a long time, but in case anyone bumps into this problem, a showDelay attribute was added to the overlayPanel to solve this problem starting from Primefaces 6.2. However, it is not in the official documentation for some reason.

Resources