Header div invisible after manual call to (jQueryMobile) $.mobile.changePage - jquery-mobile

I'm using jQuery Mobile to write a PhoneGap application, and attempting to get an effect often found in iPhone applications: upon first load of the application (because you haven't configured it yet), the application flips over and you see the settings page.
I have all of the functional bits working as expected, but there's just one problem: the header div of my settings page is not displayed.
<div data-role="page" id="settings" data-theme="c">
<div data-role="header" data-theme="a">
<h1>Settings</h1>
<a href="#home"
data-icon="check"
class="ui-btn-right"
data-transition="flip"
data-theme="a"
>Done</a>
</div>
<div data-role="content">
<!-- ... -->
This is the code that's flipping over to the settings page:
$.mobile.changePage($("#settings"), { transition: 'flip' });
If, after the app loads and automatically flips to the settings page (with header invisible), I refresh the page, the header appears. It only goes stealth in this case when I'm manually flipping to the settings page. For example, from the home screen, if I hit the settings button, it flips to the settings page and the header is not stealthed.
I've also tried poking around the css properties of the header div and its children to see if there's any difference between the two use cases, but I can't find any.
So, what now?
Update: As requested, here's the code that's detecting it's the first time the app is running and switching to the settings page:
onDeviceReady: function () {
analytics.trackStart();
//if no show has been selected, switch to settings page
if (this.getProperty('activeShowId') === null) {
var idFromCache = cache.getItem(this.getProperty('localStoragePrefix') + 'activeShowId');
if (idFromCache === null) {
//no show selected, show settings page
$.mobile.changePage($("#settings"), { transition: 'flip' });
return;
} else {
//select the corresponding show on the settings page
var radio = $("#settings input:radio[value=" + idFromCache + "]"),
label = radio.next();
radio.prop('checked', true);
label.attr('data-theme','e').addClass('ui-btn-up-e')
.removeClass('ui-radio-off').addClass('ui-radio-on')
.find(".ui-icon").removeClass('ui-icon-radio-off').addClass('ui-icon-shadow').addClass('ui-icon-radio-on');
var titleFromCache = cache.getItem(this.getProperty('localStoragePrefix') + 'activeShowTitle');
//TODO:
if (titleFromCache === null) {
pgalert('TODO: load show info when title isn\'t cached in localStorage');
}
this.setActiveShow(idFromCache, titleFromCache);
}
}
if (this.getProperty('userEmail') === null) {
var emailFromCache = cache.getItem(this.getProperty('localStoragePrefix') + 'userEmail');
if (emailFromCache === null) {
$.mobile.changePage($("#settings"), { transition: 'flip' });
return;
} else {
//fill in the email text field so it's already there if they go to the settings page
$("#settings #userEmail").val( emailFromCache );
}
}
if ( window && window.plugins && window.plugins.pushNotification && window.plugins.pushNotification.registerCallback ) {
window.plugins.pushNotification.registerCallback(
function () {
console.log('pn cb registered');
}
, this.error
);
}
}

Related

ios mobile version of app on chrome and safari - input doesn't focus on first click

I have a search bar that has an customized input component. It works fine on another fixed nav but in the body of the page, it doesn't autofocus on single click.
I understand there are a ton of stack overflows and believe me, i've tried many of them (non jquery).
The problem: Single click on a div doesn't autofocus the input.
The device: All ios mobile (Safari and Chrome).
It does not happen on android mobile or on any of the desktops - onclick immediately focuses and brings up the keyboard on android mobile.
Here's some code to show how its setup
Input component
<ImportedSearchLIbrary
inputProp={
<InputComponent
autofocus
ref={inputRef}
/>
}
/>
^There is no problem with this component because it works just fine in that nav bar i mentioned.
This is how it is implemented in the body of a page>
const SearchComponent = () => {
const [show, setShow] = useState(false);
const wrapperRef = useRef(null);
const handleClose = () => {
setShow(false);
};
const handleClick = (e) => {
if (e && e.nativeEvent) e.nativeEvent.stopImmediatePropagation();
setShow(true);
const element = document.getElementById('searchID');
const yOffset = -72;
const yPosition = element.getBoundingClientRect().top + window.pageYOffset + yOffset;
const screenWidth = window.innerWidth;
window.scrollTo({
top: yPosition,
behavior: 'smooth',
});
};
useClickOutside(wrapperRef, handleClose, [show]);
return (
<section id='searchId' ref={wrapperRef}>
<Icon iconName="search" className='' />
<div onClick={(e) => handleClick(e)}>
{show ? (
<SearchInput
placeholder='search me'
showsearch={show}
/>
) : (
<p>search me</p>
)}
</div>
{show && (
<Icon iconName="close" onClick={() => handleClose()} />
)}
</section>
);
};
So you can see that when <section> is clicked, show is set to true, and the searchbar AND the close icon show up. On desktop and android mobile (chrome, brave browser, mozilla and safari on desktop), when you click the section, it will show the search bar and auto focus to type.
This image shows how it looks - the red portion is the searchbar, the white background is the section, clickable area.
What I've tried so far>>
I've tried accessing the input ref like>
const inputRef = useRef(null);
<SearchInput
placeholder='search me'
showsearch={show}
inputRef={inputRef}
/>
Then in useEffect, (with and without settimeout)
useEffect(()=> {
setTimeout(() => {
if (show && inputRef) {
inputRef.current.focus();
}
}, 100);
}, [show]);
This didn't throw any errors but it didn't work.
Then, I tried the same thing in the searchinput component so when the component loads, in useEffect I set the focus to the passed ref.
Then I tried this >
document.addEventListener('mousedown', inputRef.current.focus());
These are the main things I've tried.
Now what works, is that if i ALWAYS show the input ie remove the condition show &&, but I don't want that.
Resources I found from stackOverflow but they didn't quite work>>
Can't Set Focus in Safari
ReactJS: How-to set focus to input-element when it enters the DOM?
https://www.quora.com/Regarding-the-mobile-browser-Safari-for-both-the-iPhone-and-iPad-with-JavaScript-how-can-I-launch-the-on-screen-keyboard
If more info is needed, i'd be happy to edit my Question with it.
Any help will be appreciated.
Thanks

Anchor Links not scrolling on iPad

I'm creating a single page website on squarespace (Pacific template https://pacific-demo.squarespace.com) which has a navigation bar that appears on scroll with anchor links that scroll to their section.
Everything works fine on desktop and on mobile but on iPad the links jump to their section instead of scrolling.
(even the template has this problem, but I'd like to fix it).
I used this code which works on phones but not on ipad:
<script>
$(function() {
$('a[href*="#"]:not([href="#"])').click(function() {
if (screen.width <= 1129) {
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;
}
}
}
});
});
</script>
Any suggestion on how to sort this out?
Thank you

Create new jquery ui tab panel with close button inside the panel?

I've got a function worked out that creates a new query ui tab panel when a user clicks a button. It also creates a new tab with a close button in it, like so:
$(function newTab() {
var $tabs = $('#nav-tabs').tabs();
$('.add-tab').click(function (e) {
e.preventDefault()
var tabName = $(this).text(),
tabLink = $(this).attr('href'),
tabNumber = -1;
$tabs.find('.nav-tab-menu li a').each(function (i) {
if ($(this).text() == tabName) {
tabNumber = i;
}
});
if (tabNumber >= 0) {
$tabs.tabs('option', 'active', tabNumber)
} else {
$("<li><a href=" + tabLink + " class='ui-icon-tab-add'>" + tabName + "</a><span class='ui-icon-close' role='presentation'><span class='sr'>Remove Tab</span></span></li>")
.appendTo(".nav-tab-menu");
$("#nav-tabs").tabs("refresh");
$('#nav-tabs').tabs('option', 'active', -1);
}
return false
})
});
It works great, but this client is a total pain in the ass, and one close button isn't good enough for them - they also want one in the newly created panel as well. I've tried cloning the button and appending it to the panel, but it doesn't seem to work. Any ideas? I should mention that the content of the current tab is replaced when the user clicks a link, so the button probably needs to be inserted before the active tab panel, rather than inside it so it doesn't get removed when the content is updated.

iframe prevents iScroll scrolling on mobile Safari

I am using iScroll on my mobile enable website (using iPhone here) to scroll inside a div.
In this this div, I have an iframe with a fixed height like this:
<body>
<div id="iscroller">
<iframe id="theIframe"></iframe>
Other stuff
</div>
</body>
Now, while scrolling within the div, everything works as expected but I cannot scroll when the scrolling gesture begins on the iframe.
The problem is described here pretty well: https://github.com/cubiq/iscroll/issues/41
So, I used the css workaround from that post by applying pointer-events:none to the iframe.
Now scrolling works perfectly but I cannot click any links which are defined within the iframe because all click/touch events on the iframe seems to be blocked due to pointer-events: none.
So, I thought:
"Ok, while the user scrolls, I need pointer-events:none. If he is
not scrolling (and instead clicking), I must set pointer-events:auto
in order to let the click/touch events pass."
So I did this:
CSS
#theIframe{pointer-events:none}
JavaScript
$("#theIframe").bind("touchstart", function(){
// Enable click before click is triggered
$(this).css("pointer-events", "auto");
});
$("#theIframe").bind("touchmove", function(){
// Disable click/touch events while scrolling
$(this).css("pointer-events", "none");
});
Even adding this doesn't work:
$("#theIframe").bind("touchend", function(){
// Re-enable click/touch events after releasing
$(this).css("pointer-events", "auto");
});
No matter what I do: Either scrolling doesn't work or clicking the link inside the iframe doesn't work.
Doesn't work. Any ideas?
I found the perfect solution. Works great on iOS and Android.
The basic idea is to put a div layer on top of that iframe. This way scrolling works smoothly.
If the user wants to tap/click on an element on that iframe I simply catch that click on the layer, save the x and y coordinates and trigger a click event on the iframe's content at these coordinates:
HTML:
<div id="wrapper">
<div id="layer"></div>
<iframe id="theIframe"></iframe>
</div>
Other stuff
CSS:
#layer{
position:absolute;
opacity:0;
width:100%;
height:100%;
top:0;
left:0;
right:0;
bottom:0;
z-index:2
}
JavaScript:
$('#layer').click(function(event){
var iframe = $('#theIframe').get(0);
var iframeDoc = (iframe.contentDocument) ? iframe.contentDocument : iframe.contentWindow.document;
// Find click position (coordinates)
var x = event.offsetX;
var y = event.offsetY;
// Trigger click inside iframe
var link = iframeDoc.elementFromPoint(x, y);
var newEvent = iframeDoc.createEvent('HTMLEvents');
newEvent.initEvent('click', true, true);
link.dispatchEvent(newEvent);
});
I found a solution for this, it happens to be close to what other guys already mentioned on github but this may be useful for whoever wants to find a fast working resolution for this problem.
I'm assuming a few things, like there's only one iscroll container, here represented as ID. This is not properly tested and needs refactor. It's working in my project, but I changed it here slightly for the example but I guess you'll easily understand what you need to do:
var $iscroll = $('#iscroll');
document.addEventListener('touchstart', function(e) {
if ($iscroll.find('iframe').length > 0){
$.each($iscroll.find('iframe'), function(k,v){
var $parent = $(v).parent().first();
if ($parent.find('.preventTouch').length == 0){
$('<div class="preventTouch" style="position:absolute; z-index:2; width:100%; height:100%;"></div>')
.prependTo($parent);
};
$parent
.css('position', 'relative').css('z-index', 1);
});
$iscroll.find('.preventTouch').on('click', function(e){
e.preventDefault();
e.stopPropagation();
return false;
});
};
};
document.addEventListener('touchend', function(e) {
if ($iscroll.find('iframe').length > 0){
setTimeout(function(){
var $iscroll = $('#iscroll');
$iscroll.find('.preventTouch').remove();
$iscroll.find('iframe').css('z-index', '');
$iscroll.find('.preventTouch').off('click');
}, 400);
};
};
Thanks for looking!

Get scroll position of panel using SplitView (JQuery Mobile)

I have implemented a WebApp using SplitView - http://asyraf9.github.com/jquery-mobile/ - (and that seems to use the ScrollView component) together with jQuery Mobile. Everything works fine ...
Within the panel I have got a list of elements that should dynamically add elements when scrolling reaches the end of the list. On the iPhone I do not use SplitView but iScroll - http://cubiq.org/iscroll - and the following code to achieve this (and it is working).
HTML:
<div data-role="panel" data-id="menu" data-hash="crumbs" style="z-index: 10000;" id="Panel">
<div data-role="page" id="Root" class="Login" onscroll="console.log('onscroll');">
<div data-role="content" data-theme="d" onscroll="console.log('onscroll');">
<div class="sub">
<ul data-role="listview" data-theme="d" data-dividertheme="a" class="picListview" id="PortfolioList">
<!-- Content added dynamically -->
</ul>
</div>
</div>
</div>
</div>
Javascript:
var defaultIScrollOptions = {
useTransition: true,
onScrollStart: function() {
this.refresh();
},
onScrollEnd: function() {
if (this.elem && this.id) {
possiblyDisplayNextDocuments(this.y, this.elem, this.id);
}
}
};
iScrolls.push(new iScroll(document.getElementById("searchResults").parentNode, defaultIScrollOptions));
But when using SplitView I do not really know which event and which element to bind the listener on or how to get the scroll position. I already tried several combinations, but did not achieve good results. The best one was the following:
$("#PortfolioList").scrollstop(function(event) {
console.log("scrollstop: "+$("#PortfolioList").scrollTop());
});
My question is: Am I using the right event listener (since it already fires althgough the scrolling animation is still in use) and how do I get the scroll position?
dont use the scrollview plugin. its buggy. Use iscroll for both iOS phonegap apps as well as android. It works fine on both.
For detecting the scroll and loading new elements into the list, listen to the the 'onScrollMove' event of iscroll.
In the iscroll-wrapper.js add this-
options.onScrollMove = function(){
that.triggerHandler('onScrollMove', [this]);
};
then in your code attach a event handler to the onScrollMove event and handle adding new rows in that. onScrollMove will fire whenever you scroll.
In the handler you can find how many rows are there in your list and that which row is on the top of your view port using something like
iscrollScrollEventHandler:function(event){
var contentDiv= $('div:jqmData(id="main") .ui-page-active div[data-role*="content"] ul li' );
var totalItemsonList = contentDiv.length;
var cont =$('div:jqmData(id="main") .ui-page-active div:jqmData(role="content")');
var itemToScrollOn = totalItemsonList - x; // x is the item no. from the top u want to scroll on. u need to keep updating i guess
var docViewBottom = $(cont).scrollTop() + $(cont).height();
var itemOffset = $(contentDiv[itemToScrollOn]).offset();
if(itemOffset){
var elemTop = itemOffset.top;
if (elemTop <= docViewBottom){
event.data.callback();
}
}
}
and in the callback add the code to add new rows. hope that helps.

Resources