How can I check if an app is installed from a web-page on an iPhone - iphone

I want to create a web-page, a page that will redirect an iPhone to the App Store if the iPhone does not have the application installed, but if the iPhone has the app installed I want it to open the application.
I have already implemented a custom URL in the iPhone application, so I have a URL for the application that is something like:
myapp://
And if this URL is invalid, I want the page to redirect to the App Store. Is this possible at all?
If I don't have the application installed on the phone and write the myapp:// URL in Safari, all I get is an error message.
Even if there exists an ugly hack with JavaScript, I would really like to know.

As far as I know you can not, from a browser, check if an app is installed or not.
But you can try redirecting the phone to the app, and if nothing happens redirect the phone to a specified page, like this:
setTimeout(function () { window.location = "https://itunes.apple.com/appdir"; }, 25);
window.location = "appname://";
If the second line of code gives a result then the first row is never executed.
Similar questions:
iPhone browser: Checking if iPhone app is installed from browser
Is it possible to register a http+domain-based URL Scheme for iPhone apps, like YouTube and Maps?

To further the accepted answer, you sometimes need to add extra code to handle people returning to the browser after launching the app - that the setTimeout function will run whenever they do. So, I do something like this:
var now = new Date().valueOf();
setTimeout(function () {
if (new Date().valueOf() - now > 100) return;
window.location = "https://itunes.apple.com/appdir";
}, 25);
window.location = "appname://";
That way, if there has been a freeze in code execution (i.e., app switching), it won't run.

iOS Safari has a feature that allows you to add a "smart" banner to your webpage that will link either to your app, if it is installed, or to the App Store.
You do this by adding a meta tag to the page. You can even specify a detailed app URL if you want the app to do something special when it loads.
Details are at Apple's Promoting Apps with Smart App Banners page.
The mechanism has the advantages of being easy and presenting a standardized banner. The downside is that you don't have much control over the look or location. Also, all bets are off if the page is viewed in a browser other than Safari.

As of 2017, it seems there's no reliable way to detect an app is installed, and the redirection trick won't work everywhere.
For those like me who need to deep-link directly from emails (quite common), it is worth noting the following:
Sending emails with appScheme:// won't work fine because the links will be filtered in Gmail
Redirecting automatically to appScheme:// is blocked by Chrome: I suspect Chrome requires the redirection to be synchronous to user interaction (like a click)
You can now deep link without appScheme:// and it's better but it requires a modern platform and additional setup. Android iOS
It is worth noting that other people already thought about this in-depth. If you look at how Slack implements his "magic link" feature, you can notice that:
It sends an email with a regular HTTP link (ok with Gmail)
The web page has a big button that links to appScheme:// (ok with Chrome)

#Alistair pointed out in this answer that sometimes users will return to the browser after opening the app. A commenter to that answer indicated that the times values used had to be changed depending on iOS version.
When our team had to deal with this, we found that the time values for the initial timeout and telling whether we had returned to the browser had to be tuned, and often didn't work for all users and devices.
Rather than using an arbitrary time difference threshold to determine whether we had returned to the browser, it made sense to detect the "pagehide" and "pageshow" events.
I developed the following web page to help diagnose what was going on. It adds HTML diagnostics as the events unfold, mainly because using techniques like console logging, alerts, or Web Inspector, jsfiddle.net, etc. all had their drawbacks in this work flow. Rather than using a time threshold, the JavaScript counts the number of "pagehide" and "pageshow" events to see whether they have occurred. And I found that the most robust strategy was to use an initial timeout of 1000 (rather than the 25, 50, or 100 reported and suggested by others).
This can be served on a local server, e.g. python -m SimpleHTTPServer and viewed on iOS Safari.
To play with it, press either the "Open an installed app" or "App not installed" links. These links should cause respectively the Maps app or the App Store to open. You can then return to Safari to see the sequence and timing of the events.
(Note: this will work for Safari only. For other browsers (like Chrome) you'd have to install handlers for the pagehide/show-equivalent events).
Update: As #Mikko has pointed out in the comments, the pageshow/pagehide events we are using are apparently no longer supported in iOS8.
<html>
<head>
</head>
<body>
Open an installed app
<br/><br/>
App not installed
<br/>
<script>
var hideShowCount = 0 ;
window.addEventListener("pagehide", function() {
hideShowCount++;
showEventTime('pagehide');
});
window.addEventListener("pageshow", function() {
hideShowCount++;
showEventTime('pageshow');
});
function clickHandler(){
var hideShowCountAtClick = hideShowCount;
showEventTime('click');
setTimeout(function () {
showEventTime('timeout function ' + (hideShowCount-hideShowCountAtClick) + ' hide/show events');
if (hideShowCount == hideShowCountAtClick){
// app is not installed, go to App Store
window.location = 'http://itunes.apple.com/app';
}
}, 1000);
}
function currentTime()
{
return Date.now()/1000;
}
function showEventTime(event){
var time = currentTime() ;
document.body.appendChild(document.createElement('br'));
document.body.appendChild(document.createTextNode(time + ' ' + event));
}
</script>
</body>
</html>

You can check out this plugin that tries to solve the problem. It is based on the same approach as described by missemisa and Alastair etc, but uses a hidden iframe instead.
https://github.com/hampusohlsson/browser-deeplink

I needed to do something like this, and I ended up going with the following solution.
I have a specific website URL that will open a page with two buttons
Button one go to the website
Button two go to the application (iPhone / Android phone / tablet). You can fall back to a default location from here if the app is not installed (like another URL or an app store)
Cookie to remember the user's choice
<head>
<title>Mobile Router Example </title>
<script type="text/javascript">
function set_cookie(name,value)
{
// JavaScript code to write a cookie
}
function read_cookie(name) {
// JavaScript code to read a cookie
}
function goToApp(appLocation) {
setTimeout(function() {
window.location = appLocation;
// This is a fallback if the app is not installed.
// It could direct to an app store or a website
// telling user how to get the app
}, 25);
window.location = "custom-uri://AppShouldListenForThis";
}
function goToWeb(webLocation) {
window.location = webLocation;
}
if (readCookie('appLinkIgnoreWeb') == 'true' ) {
goToWeb('http://somewebsite');
}
else if (readCookie('appLinkIgnoreApp') == 'true') {
goToApp('http://fallbackLocation');
}
</script>
</head>
<body>
<div class="iphone_table_padding">
<table border="0" cellspacing="0" cellpadding="0" style="width:100%;">
<tr>
<td class="iphone_table_leftRight"> </td>
<td>
<!-- Intro -->
<span class="iphone_copy_intro">Check out our new app or go to website</span>
</td>
<td class="iphone_table_leftRight"> </td>
</tr>
<tr>
<td class="iphone_table_leftRight"> </td>
<td>
<div class="iphone_btn_padding">
<!-- Get iPhone app button -->
<table border="0" cellspacing="0" cellpadding="0" class="iphone_btn" onclick="set_cookie('appLinkIgnoreApp',document.getElementById('chkDontShow').checked);goToApp('http://getappfallback')">
<tr>
<td class="iphone_btn_on_left"> </td>
<td class="iphone_btn_on_mid">
<span class="iphone_copy_btn">
Get The Mobile Applications
</span>
</td>
<td class="iphone_btn_on_right"> </td>
</tr>
</table>
</div>
</td>
<td class="iphone_table_leftRight"> </td>
</tr>
<tr>
<td class="iphone_table_leftRight"> </td>
<td>
<div class="iphone_btn_padding">
<table border="0" cellspacing="0" cellpadding="0" class="iphone_btn" onclick="set_cookie('appLinkIgnoreWeb',document.getElementById('chkDontShow').checked);goToWeb('http://www.website.com')">
<tr>
<td class="iphone_btn_left"> </td>
<td class="iphone_btn_mid">
<span class="iphone_copy_btn">
Visit Website.com
</span>
</td>
<td class="iphone_btn_right"> </td>
</tr>
</table>
</div>
</td>
<td class="iphone_table_leftRight"> </td>
</tr>
<tr>
<td class="iphone_table_leftRight"> </td>
<td>
<div class="iphone_chk_padding">
<!-- Check box -->
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td><input type="checkbox" id="chkDontShow" /></td>
<td>
<span class="iphone_copy_chk">
<label for="chkDontShow"> Don’t show this screen again.</label>
</span>
</td>
</tr>
</table>
</div>
</td>
<td class="iphone_table_leftRight"> </td>
</tr>
</table>
</div>
</body>
</html>

After compiling a few answers, I've come up with the following code. What surprised me was that the timer does not get frozen on a PC (Chrome and Firefox) or Android Chrome - the trigger worked in the background, and the visibility check was the only reliable information.
var timestamp = new Date().getTime();
var timerDelay = 5000;
var processingBuffer = 2000;
var redirect = function(url) {
//window.location = url;
log('ts: ' + timestamp + '; redirecting to: ' + url);
}
var isPageHidden = function() {
var browserSpecificProps = {hidden:1, mozHidden:1, msHidden:1, webkitHidden:1};
for (var p in browserSpecificProps) {
if(typeof document[p] !== "undefined"){
return document[p];
}
}
return false; // Actually inconclusive, assuming not
}
var elapsedMoreTimeThanTimerSet = function(){
var elapsed = new Date().getTime() - timestamp;
log('elapsed: ' + elapsed);
return timerDelay + processingBuffer < elapsed;
}
var redirectToFallbackIfBrowserStillActive = function() {
var elapsedMore = elapsedMoreTimeThanTimerSet();
log('hidden:' + isPageHidden() + '; time: ' + elapsedMore);
if (isPageHidden() || elapsedMore) {
log('not redirecting');
}else{
redirect('appStoreUrl');
}
}
var log = function(msg){
document.getElementById('log').innerHTML += msg + "<br>";
}
setTimeout(redirectToFallbackIfBrowserStillActive, timerDelay);
redirect('nativeApp://');
JS Fiddle

The following answer still works, tested on iOS 10 through 14. It builds upon earlier answers. I added window.close() to get rid of the empty tab window that was left behind in browsers after the redirects or page return. If fixes 2 of the 4 scenarios where a blank tab would be left behind....maybe someone else can fix the 3rd & 4th
<script>
var now = new Date().valueOf();
setTimeout(function () {
// time stamp comaprison prevents redirecting to app store a 2nd time
if (new Date().valueOf() - now > 100) {
window.close() ; // scenario #4
// old way - "return" - but this would just leave a blank page in users browser
//return;
}
if (isIOS == 1) {
// still can't avoid the "invalid address" safari pops up
// but at least we can explain it to users
var msg = "'invalid address' = MyApp NOT DETECTED.\n\nREDIRECTING TO APP STORE" ;
} else {
var msg = "MyApp NOT DETECTED\n\nREDIRECTING TO APP STORE" ;
}
if (window.confirm(msg)) {
window.location = "<?=$storeUrl?>";
// scenario #2 - will leave a blank tab in browser
} else {
window.close() ; // scenario #3
}
}, 50);
window.location = "<?=$mobileUrl?>";
// scenario #1 - this will leave a blank tab
</script>

I have been trying to achieve the same in a Safari extension for iOS15. It seems that all previous strategies fail - the "Open in" dialog and the "Invalid address" one are completely equal, both non-blocking, so the timer-based solutions offer inconsistent results, depending on the time it takes to load the page.
My workaround was to create an app store redirect message within a modal popup that imitates the appearance of the system prompt, hide it behind the system prompt, and dismiss it with an event listener when the tab loses focus. There are two remaining problems with the UX:
There is no way to suppress the "Invalid address" prompt. All we can do (if we don't go the Universal Links path) is to explain it afterwards with our own prompt.
If the user chooses "Cancel" from the "Open in" prompt, he or she is still presented with our redirect prompt.
The following code benefitted both from the answers above and from this SO code for creating a modal popup.
// Change the following vars to suit your needs
var my_app_name = "My App";
var my_app_id = "id1438151717"
var my_app_scheme = "myapp://do.this"
function toggleModal(isModal, inputs, elems, msg) {
for (const input of inputs) input.disabled = isModal;
modal.style.display = isModal ? "block" : "none";
elems[0].textContent = isModal ? msg : "";
}
function myConfirm(msg) {
const inputs = [...document.querySelectorAll("input, textarea, select")].filter(input => !input.disabled);
const modal = document.getElementById("modal");
const elems = modal.children[0].children;
return new Promise((resolve) => {
toggleModal(true, inputs, elems, msg);
elems[3].onclick = () => resolve(true);
elems[4].onclick = () => resolve(false);
}).then(result => {
toggleModal(false, inputs, elems, msg);
return result;
});
}
function redirectMessage() {
var r = myConfirm("To download " + my_app_name + ", tap OK.");
return r.then(ok => {
if (ok) {
console.log("Redirecting to the App Store...");
window.location = "itms-apps://itunes.apple.com/app/" + my_app_id;
} else {
console.log("User cancelled redirect to the App Store");
}
return ok;
});
}
function prepareListener() {
document.addEventListener("visibilitychange", function() {
const inputs = [...document.querySelectorAll("input, textarea, select")].filter(input => !input.disabled);
const modal = document.getElementById("modal");
const elems = modal.children[0].children;
if (!document.hasFocus()) {
console.log("User left tab. Closing modal popup")
toggleModal(false, inputs, elems, "");
}
});
}
function onTap() {
setTimeout(function() {
// We can't avoid the "invalid address" Safari popup,
// but at least we can explain it to users.
// We will create a modal popup behind it, which the
// event listener will close automatically if the app
// opens and we leave the tab
redirectMessage()
}, 50);
window.location = my_app_scheme;
}
prepareListener()
#modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background: rgb(0, 0, 0);
background: rgba(0, 0, 0, 0.4);
font-family: "ms sans serif", arial, sans-serif;
font-size: medium;
border-radius: 15px;
}
#modal>div {
position: relative;
padding: 10px;
width: 320px;
height: 60px;
margin: 0 auto;
top: 50%;
margin-top: -45px;
background: white;
border: 2px outset;
border-radius: 15px;
}
#cancel_button {
position: fixed;
right: 50%;
margin-right: -95px;
bottom: 50%;
margin-bottom: -32px;
padding: 0;
border: none;
background: none;
color: rgb(0, 122, 255);
font-size: medium;
font-weight: normal;
}
#ok_button {
position: fixed;
right: 50%;
margin-right: -140px;
bottom: 50%;
margin-bottom: -32px;
padding: 0;
border: none;
background: none;
color: rgb(0, 122, 255);
font-size: medium;
font-weight: semi-bold;
}
<div id="modal">
<div>
<div></div><br><br>
<button id="ok_button">OK</button>
<button id="cancel_button">Cancel</button>
</div>
</div>
<p> Tap here to open app </p>

The date solution is much better than others. I had to increment the time to 50 like that.
This is a Twitter example:
// On click of your event handler...
var twMessage = "Your Message to share";
var now = new Date().valueOf();
setTimeout(function () {
if (new Date().valueOf() - now > 100)
return;
var twitterUrl = "https://twitter.com/share?text=" + twMessage;
window.open(twitterUrl, '_blank');
}, 50);
window.location = "twitter://post?message=" + twMessage;
The only problem on mobile iOS Safari is when you don't have the app installed on the device, and so Safari shows an alert that autodismisses when the new URL is opened. Anyway, it is a good solution for now!

I didn't read all of these answers, but you may be use an iframe and adding the source to, "my app://whatever".
Then check regularly on a set interval of the page is 404 or not.
You could also use an Ajax call. If there is a 404 response then the app is not installed.

Related

Is there any way to navigate from Visual studio code extension's webview to external Windows application?

I am developing a Visual Studio Code extension which opens a webview on some event. Webview has some links with the below link address:
reqtify:////NKR8WIN764PLP/Users//Public/Documents/Reqtify/2020x/Examples/Coupling/Code/Code%20C/Code%20Sample.rqtf?id=SPEC_2
With this URI, I should be navigated to an external Windows application. When I use this URI in Chrome/Firefox browser, I am able to navigate to the targeted external Windows application. But from within my extension's webview, when I hit this link, I am getting below error:
Uncaught Error: [UriError]: If a URI does not contain an authority component, then the path cannot begin with two slash characters ("//")
I tried to replace all the "//" with "/" making the URI :
reqtify:/NKR8WIN764PLP/Users/Public/Documents/Reqtify/2020x/Examples/Coupling/Code/Code%20C/Code%20Sample.rqtf?id=SPEC_2
But still, I am not able to navigate to external Windows application.
Here is my code to render the webview:
protected async doUpdate(): Promise<void> {
let content: string;
this.throttleTimer = undefined;
content = await this.contentProvider.provideRequirementsContent(document.uri,
this.state, this.editor.webview);
this.editor.webview.options = getWebviewOptions(this.contentProvider);
this.editor.webview.html = content;
}
protected static getWebviewOptions(contentProvider: BaseContentProvider
): vscode.WebviewOptions {
return {
enableScripts: true,
localResourceRoots: contentProvider.localResourceRoots
};
}
public async provideRequirementsContent(sourceUri: vscode.Uri, state: any, webview: any): Promise<string> {
let html = "<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
</head>
<body>
<div class="sect2">
<h2>
<span class="numbering">1. </span>Requirements of Specifications</h2>
<table width="100%" align="center">
<colgroup>
<col width="80"></col>
<col width="1*"></col></colgroup>
<thead>
<tr>
<th>Requirement</th>
<th>Text</th></tr></thead>
<tbody>
<tr>
<td>SPEC_1</td>
<td>For any of the cruise control (CC) functions to take effect, CC must be turned on first.</td></tr>
<tr>
<td>SPEC_2</td>
<td>CC can be in the following states: off, enabled (i.e., on and cruising), and disabled (on, but not cruising).</td></tr>
<tr>
<td>SPEC_3</td>
<td>The CC system should be automatically disabled below 30mph and above 90mph.</td></tr>
<tr>
<td>SPEC_4</td>
<td>Four actions are permitted during CC: set speed, accelerate, decelerate, and resume speed.</td></tr>
<tr>
<td>SPEC_5</td>
<td>When the system is under CC and the brake is pressed, CC is disabled. When the resume button is pressed, the system resumes at the last set CC speed.</td></tr>
<tr>
<td>SPEC_6</td>
<td>When the system is under CC and the accelerator pedal is pressed, CC is disabled and the speed increases correspondingly. When the accelerator is released, the CC resumes at its last set CC speed. If at any point of time during acceleration the CC speed is set, CC replaces the old set speed with the new speed.</td></tr>
<tr>
<td>SPEC_7</td>
<td>If CC is enabled and the vehicle starts going uphill or downhill, CC should automatically apply the accelerator or brake to maintain the set speed.</td></tr>
<tr>
<td>SPEC_8</td>
<td>You can assume an automatic transmission vehicule.</td></tr></tbody></table></div>
</body>
</html>
"
html = html.split('reqtify:////').join('reqtify:/');
html = html.split('//Public').join('/Public');
}
I want to navigate to an external Windows application with these HTML content links. Could you please help?
After some research, I managed to solve this in some way, but I am not sure whether it is correct way.
I appended a script to above HTML string which contains a method that is called on load event of body.
So, my final HTML string will be like:
let html = "<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
</head>
<body onload="myFunction()>
<div class="sect2">
<h2>
<span class="numbering">1. </span>Requirements of Specifications</h2>
<table width="100%" align="center">
<colgroup>
<col width="80"></col>
<col width="1*"></col></colgroup>
<thead>
<tr>
<th>Requirement</th>
<th>Text</th></tr></thead>
<tbody>
<tr>
<td>SPEC_1</td>
<td>For any of the cruise control (CC) functions to take effect, CC must be turned on first.</td></tr>
<tr>
<td>SPEC_2</td>
<td>CC can be in the following states: off, enabled (i.e., on and cruising), and disabled (on, but not cruising).</td></tr>
<tr>
<td>SPEC_3</td>
<td>The CC system should be automatically disabled below 30mph and above 90mph.</td></tr>
<tr>
<td>SPEC_4</td>
<td>Four actions are permitted during CC: set speed, accelerate, decelerate, and resume speed.</td></tr>
<tr>
<td>SPEC_5</td>
<td>When the system is under CC and the brake is pressed, CC is disabled. When the resume button is pressed, the system resumes at the last set CC speed.</td></tr>
<tr>
<td>SPEC_6</td>
<td>When the system is under CC and the accelerator pedal is pressed, CC is disabled and the speed increases correspondingly. When the accelerator is released, the CC resumes at its last set CC speed. If at any point of time during acceleration the CC speed is set, CC replaces the old set speed with the new speed.</td></tr>
<tr>
<td>SPEC_7</td>
<td>If CC is enabled and the vehicle starts going uphill or downhill, CC should automatically apply the accelerator or brake to maintain the set speed.</td></tr>
<tr>
<td>SPEC_8</td>
<td>You can assume an automatic transmission vehicule.</td></tr></tbody></table></div>
<script>
function myFunction() {
const vscode = acquireVsCodeApi();
let reqs = document.getElementsByTagName('a');
for (let item of reqs) {
item.addEventListener('click', function() {
vscode.postMessage({
command: 'open',
text: item.getAttribute('href')
})
return false;
}, false);
}
}
</script>
</body>
</html>
"
If you observe the script, I am using vscode.postMessage({}) to send a message [containing command "open" and url as data] which will be received by onDidReceiveMessage method of webview as below:
this.editor.webview.onDidReceiveMessage(message => {
switch (message.command) {
case 'open':
var url = message.text;
var start = (process.platform === 'darwin'? 'open': process.platform === 'win32'? 'start': 'xdg-open');
require('child_process').exec(start + ' ' + url);
}
}, null, super.disposables);
As and when "open" command is received from postMessage, I am navigating to external application using url with the help of Nodejs API.
Thank you.

how to select toggle on/off button in protractor

I am running E2E on my angular application using Protractor. How to write script for toggle button.
<span class="bootstrap-switch-handle-on bootstrap-switch-primary" style="width: 41px;">ON</span>
<span class="bootstrap-switch-label" style="width: 41px;"> </span>
<span class="bootstrap-switch-handle-off bootstrap-switch-default" style="width: 41px;">OFF</span>
I can't guess the DOM of your page. Firstly, I'd rather not use xpath. IMHO in protractor by should be lowercase.
I assume that after clicking the element class of span changes.
maybe try sth like that:
const button = element(by.css('span.bootstrap-switch-primary'));
button.getAttribute('class').then((classes) => {
if (classes.indexOf('bootstrap-switch-handle-on') === -1) {
return button.click();
}
}

Button can't be clicked during automation

I am trying to click a button which is displayed/visible/present.
When doing it manually, user was able to click the button.
If the test was executed, you will notice that it is trying to click the button, but nothing is happening.
I also tried to put a very long wait and tried to click it manually during automation.
But, when clicking it, nothing also happens.
I cannot share the site since it is in a proxy.
This is the HTML of the button and it looks normal:
<a class="x-btn x-unselectable x-box-item x-toolbar-item x-btn-default-small" style="min-width: 75px; right: auto; left: 328px; top: 0px; margin: 0px;" hidefocus="on" unselectable="on" role="button" aria-hidden="false" aria-disabled="false" id="button-1011" tabindex="-1" data-componentid="button-1011">
<span id="button-1011-btnWrap" data-ref="btnWrap" role="presentation" unselectable="on" style="" class="x-btn-wrap x-btn-wrap-default-small ">
<span id="button-1011-btnEl" data-ref="btnEl" role="presentation" unselectable="on" style="" class="x-btn-button x-btn-button-default-small x-btn-text x-btn-button-center ">
<span id="button-1011-btnIconEl" data-ref="btnIconEl" role="presentation" unselectable="on" class="x-btn-icon-el x-btn-icon-el-default-small " style=""></span>
<span id="button-1011-btnInnerEl" data-ref="btnInnerEl" unselectable="on" class="x-btn-inner x-btn-inner-default-small">Save</span>
</span>
</span>
</a>
Code:
global.elmCBSave = element.all(by.cssContainingText('span', 'Save')).last();
it('should click the Save button.', function() {
global.elmCBSave.click();
});
I also tried using browser.executeSrcipt, this is working when executed thru console:
browser.executeScript('$(".x-btn-inner.x-btn-inner-default-small:eq(3)").click()')
There are a few things you can try.
Be sure to pass done into your callback and handle the returned promise from the click event
it('should click the Save button.', function(done) {
global.elmCBSave.click().then(function(){
done();
});
});
Also, I tend to put an expected condition to wait for element to be clickable: http://www.protractortest.org/#/api?view=ProtractorExpectedConditions.prototype.elementToBeClickable
var EC = protractor.ExpectedConditions;
// Waits for the element with id 'abc' to be clickable.
it('should click the Save button.', function(done) {
browser.wait(EC.elementToBeClickable($('#abc')), 5000).then(function(){
global.elmCBSave.click().then(function(){
done();
});
});
});

jQuery Click on next element

I have a problem with programming jQuery and I hope that you can help me even if a speak english like an alien %).
I'm doing a site with horizontal scrolling, you can see it here http://www.kinetics.ru/index_kin.html
I have a menu at the left and two buttons PREV and NEXT at the bottom.
Menu is working correctly, but I need to let the bottom buttons work.
By clicking for example on NEXT I need that code made a click on next menu btn after the one that have style 'on'.
The code is:
<div class="menu">
<ul class="nav">
<li id="main"><img src="img/menu/about.png" /></li>
<li><img src="img/menu/uslugi.png"/></li>
<li><img src="img/menu/portfolio.png"/></li>
<li><img src="img/menu/clients.png"/></li>
<li><img src="img/menu/contacts.png"/></li>
</ul>
</div>
...
<div class="bottomNav" style="position: absolute; z-index: 11">
<div style="height: 26px; width:98px; margin:-75px 0 0 500px; position: fixed" class="back"><img src="img/back.png"</div>
<div style="height: 26px; width:114px; margin:-25px 0 0 600px; position: fixed" id="next"><img src="img/forward.png"</div>
And my not working jQuery code for next btn:
$('bottomNav, #next img').click(function(){
if ($('ul.nav').find('img').hasClass('on')){
$('ul.nav').next().click();
}
});
Also I've tried to do like this:
$('bottomNav, #next img').click(function(){
$('ul.nav img').click(function(){
if ($(this).hasClass('on')){
$(this).next().click();}
});
});
P.S. Sorry for my noobish question. I just got a task at work to make a site and nobody cares that a designer is NOT the same as web-designer. I have no possibility to learn first, I have to learn and do at same time.
You have to review jQuery selectors and as well your HTML code, it's terribly wrong. Firebug says:
I modified your code in proper way:
<div class="bottomNav" style="position: absolute; z-index: 11">
<div id="prev" style="height: 26px; width:98px; margin:-75px 0 0 500px; position: fixed">
<img src="img/back.png" />
</div>
<div id="next" style="height: 26px; width:114px; margin:-25px 0 0 600px; position: fixed" >
<img src="img/forward.png" />
</div>
</div>
JavaScript (It's pretty simple and I didn't test it, but it should give you an idea).
$('#prev').click(function () {
var $activeLi = getActiveLi();
var $prevLi = $parentLi.prev();
if ($prevLi.length > 0) {
$prevLi.find('a').click();
}
});
$('#next').click(function () {
var $activeLi = getActiveLi();
var $nextLi = $parentLi.next();
if ($nextLi.length > 0) {
$nextLi.find('a').click();
}
});
function getActiveLi() {
var $activeImg = $('.menu .nav img.on');
var $activeLi = $activeImg.closest('li');
return $activeLi;
};
But beeing seriously: before continuing developing your web site you seriously should put some effort in learnin hot to use jQuery and how to structure HTML...
Solved!
Thank you again!
Your code was really helpfull.
I have modified it a little bit for my needs and also changed in line
var $activeLi = $activeImg.closest('li');
closest('li') to parents('li'), cause it wasn't working like that.
So now code looks like this:
$('#prev').click(function () {
var $prevLi = getActiveLi().prev();
if ($prevLi.length > 0){$prevLi.find('img').click();}
});
$('#next').click(function () {
var $nextLi = getActiveLi().next();
if ($nextLi.length > 0){$nextLi.find('img').click();}
});
function getActiveLi() {
var $activeImg = $('.menu .nav img.on');
var $activeLi = $activeImg.parents('li');
return $activeLi;
};
Also i guess, that i have understood why firebug was showing so big disaster in code... i have forgot to close the tag :)))
Anyway...MANY THANKS TO YOU!
P.S. third day reading a giant book about jQuery :)

In Dashcode, with iPhone webkit, how to add a new page?

I'm working on a website for iphone using dashcode. But i don't know how to add a new html page to the 'site'. I could use stackLayout but it takes so much time to load the 'index.html' since it has all the stackLayout's views in it.
enlightenment needed :)
rather than adding a new html page you could split the existing index.html into "pages" with each page in a and then use the javascript to manipulate the visibility of the divs.
so the javascript looks like this
function hide_me(el) {
if(document.getElementById(el)) {
if(document.getElementById(el).style.display != "none")
document.getElementById(el).style.display = "none";
}
}
function show_me(el) {
if(document.getElementById(el)) {
if(document.getElementById(el).style.display == "none")
document.getElementById(el).style.display = "block";
}
}
then in the html
<ul>
<li><a href="javascript:
show_me('one');
hide_me('two');
hide_me('three');
etc......
</li>
</ul>
and further down the index.html
<div class="mainContent" id="one">
<!-- generated html -->
<table>
<tbody>
<tr class="head">
<td class="item" colspan="2">
CHMOD
</td>
</tr>
etc .....
</table>
</div>
This way the load is all taken when the widget is fired up and therafter you just display the divs as opposed to having to load them
Click:
FILE > NEW > FILE
Then, rename the file to "whatever.html"
You're welcome!
My site was made with Dashcode!