Notify panel similar to stackoverflow's - asp.net-mvc

Remember the little div that shows up at the top of the page to notify us of things (like new badges)?
I would like to implement something like that as well and am looking for some best practices or patterns.
My site is an ASP.NET MVC app as well. Ideally the answers would include specifics like "put this in the master page" and "do this in the controllers".
Just to save you from having to look yourself, this is the code I see from the welcome message you get when not logged in at stackoverflow.
<div class="notify" style="">
<span>
First time at Stack Overflow? Check out the
FAQ!
</span>
<a class="close-notify" onclick="notify.close(true)" title="dismiss this notification">×</a>
</div>
<script type="text/javascript">
$().ready(function() {
notify.show();
});
</script>
I'd like to add that I understand this perfectly and also understand the jquery involvement. I'm just interested in who puts the code into the markup and when ("who" as in which entities within an ASP.NET MVC app).
Thanks!

This answer has a complete solution.
Copy-pasting:
This is the markup, initially hidden so we can fade it in:
<div id='message' style="display: none;">
<span>Hey, This is my Message.</span>
X
</div>
Here are the styles applied:
#message {
font-family:Arial,Helvetica,sans-serif;
position:fixed;
top:0px;
left:0px;
width:100%;
z-index:105;
text-align:center;
font-weight:bold;
font-size:100%;
color:white;
padding:10px 0px 10px 0px;
background-color:#8E1609;
}
#message span {
text-align: center;
width: 95%;
float:left;
}
.close-notify {
white-space: nowrap;
float:right;
margin-right:10px;
color:#fff;
text-decoration:none;
border:2px #fff solid;
padding-left:3px;
padding-right:3px
}
.close-notify a {
color: #fff;
}
And this is javascript (using jQuery):
$(document).ready(function() {
$("#message").fadeIn("slow");
$("#message a.close-notify").click(function() {
$("#message").fadeOut("slow");
return false;
});
});
And voila. Depending on your page setup you might also want to edit the body margin-top on display.
Here is a demo of it in action.

After snooping around the code a bit, here's a guess:
The following notification container is always in the view markup:
<div id="notify-container"> </div>
That notification container is hidden by default, and is populated by javascript given certain circumstances. It can contain any number of messages.
If the user is not logged in
Persistence: Cookies are used to keep track of whether a message is shown or not.
Server side generated code in the view:
I think stackoverflow only shows one message if you aren't logged in. The following code is injected into the view:
<script type="text/javascript">
$(function() { notify.showFirstTime(); });
</script>
The showFirstTime() javascript method just determines whether to show the "Is this your first time here?" message based on whether a cookie has been set or not. If there is no cookie, the message is shown. If the user takes action, the cookie is set, and the message won't be show in the future. The nofity.showFirstTime() function handles checking for the cookie.
If the user is logged in
Persistence: The database is used to keep track of whether a message has been shown or not.
Server side generated code in the view:
When a page is requested, the server side code checks the database to see what messages need to be displayed. The server side code then injects messages in json format into the view and puts a javascript call to showMessages().
For example, if I am logged into a view, I see the following in the markup at SO:
<script type="text/javascript">
1
2 var msgArray = [{"id":49611,"messageTypeId":8,"text":"Welcome to Super User! Visit your \u003ca href=\"/users/00000?tab=accounts\"\u003eaccounts tab\u003c/a\u003e to associate with our other websites!","userId":00000,"showProfile":false}];
3 $(function() { notify.showMessages(msgArray); });
4
</script>
So the server side code either injects code to call the "showFirstTime" method if the user is not logged in or it injects messages and calls "showMessages" for a logged in user.
More about the client side code
The other key component is the "notify" JavaScript module Picflight has de-minified (you can do the same using yslow for firebug). The notify module handles the populating of the notification div based on the server side generated javascript.
Not logged in, client side
If the user is not logged in, then the module handles events when the user X's out the notification or goes to the FAQ by creating a cookie. It also determines whether to display the first time message by checking for a cookie.
Logged in, client side
If the user is logged in, the notify module adds all the messages generated by the server into the notification div. It also most likely uses ajax to update the database when a user dismisses a message.

Though these are by no means official, the common practices that I follow would result in something like this:
Create the element that will act as the notification container in the markup, but hide it by default (this can be done numerous ways - JavaScript, external CSS, or inline styles).
Keep the scripts responsible for the behavior of the notification outside of the markup. In the example above, you can see there is an onclick as well as another function that fires on page load contained in the markup. Though it works, I see this as mixing presentation and behavior.
Keep the notification message's presentation contained in an external stylesheet.
Again, these are just my common practices stated in the context of your question. The thing with web development, as the nature of your question already shows, is that there are so many ways to do the same thing with the same results.

I see the following jQuery function? I beleive that injects the html into the div with id notify-container.
I don't understand how this JS is used and called based on certain events, perhaps someone can explain.
var notify = function() {
var d = false;
var e = 0;
var c = -1;
var f = "m";
var a = function(h) {
if (!d) {
$("#notify-container").append('<table id="notify-table"></table>');
d = true
}
var g = "<tr" + (h.messageTypeId ? ' id="notify-' + h.messageTypeId + '"' : "");
g += ' class="notify" style="display:none"><td class="notify">' + h.text;
if (h.showProfile) {
var i = escape("/users/" + h.userId);
g += ' See your profile.'
}
g += '</td><td class="notify-close"><a title="dismiss this notification" onclick="notify.close(';
g += (h.messageTypeId ? h.messageTypeId : "") + ')">×</a></td></tr>';
$("#notify-table").append(g)
};
var b = function() {
$.cookie("m", "-1", {
expires: 90,
path: "/"
})
};
return {
showFirstTime: function() {
if ($.cookie("new")) {
$.cookie("new", "0", {
expires: -1,
path: "/"
});
b()
}
if ($.cookie("m")) {
return
}
$("body").css("margin-top", "2.5em");
a({
messageTypeId: c,
text: 'First time here? Check out the <a onclick="notify.closeFirstTime()">FAQ</a>!'
});
$(".notify").fadeIn("slow")
},
showMessages: function(g) {
for (var h = 0; h < g.length; h++) {
a(g[h])
}
$(".notify").fadeIn("slow");
e = g.length
},
show: function(g) {
$("body").css("margin-top", "2.5em");
a({
text: g
});
$(".notify").fadeIn("slow")
},
close: function(g) {
var i;
var h = 0;
if (g && g != c) {
$.post("/messages/mark-as-read", {
messagetypeid: g
});
i = $("#notify-" + g);
if (e > 1) {
h = parseInt($("body").css("margin-top").match(/\d+/));
h = h - (h / e)
}
} else {
if (g && g == c) {
b()
}
i = $(".notify")
}
i.children("td").css("border-bottom", "none").end().fadeOut("fast", function() {
$("body").css("margin-top", h + "px");
i.remove()
})
},
closeFirstTime: function() {
b();
document.location = "/faq"
}
}
} ();

StackOverflow uses jQuery - the JS code you posted from SO is a jQuery call. It'll do exactly what you want with almost no code. Highly recommended.

I wrote this piece of Javascript that does just that including stacking, staying with you as you scroll like Stack Overflow's does and pushing the whole page down whenever a new bar is added. The bars also expire. The bars also slide into existence.
// Show a message bar at the top of the screen to tell the user that something is going on.
// hideAfterMS - Optional argument. When supplied it hides the bar after a set number of milliseconds.
function AdvancedMessageBar(hideAfterMS) {
// Add an element to the top of the page to hold all of these bars.
if ($('#barNotificationContainer').length == 0)
{
var barContainer = $('<div id="barNotificationContainer" style="width: 100%; margin: 0px; padding: 0px;"></div>');
barContainer.prependTo('body');
var barContainerFixed = $('<div id="barNotificationContainerFixed" style="width: 100%; position: fixed; top: 0; left: 0;"></div>');
barContainerFixed.prependTo('body');
}
this.barTopOfPage = $('<div style="margin: 0px; background: orange; width: 100%; text-align: center; display: none; font-size: 15px; font-weight: bold; border-bottom-style: solid; border-bottom-color: darkorange;"><table style="width: 100%; padding: 5px;" cellpadding="0" cellspacing="0"><tr><td style="width: 20%; font-size: 10px; font-weight: normal;" class="leftMessage" ></td><td style="width: 60%; text-align: center;" class="messageCell"></td><td class="rightMessage" style="width: 20%; font-size: 10px; font-weight: normal;"></td></tr></table></div>');
this.barTopOfScreen = this.barTopOfPage.clone();
this.barTopOfPage.css("background", "transparent");
this.barTopOfPage.css("border-bottom-color", "transparent");
this.barTopOfPage.css("color", "transparent");
this.barTopOfPage.prependTo('#barNotificationContainer');
this.barTopOfScreen.appendTo('#barNotificationContainerFixed');
this.setBarColor = function (backgroundColor, borderColor) {
this.barTopOfScreen.css("background", backgroundColor);
this.barTopOfScreen.css("border-bottom-color", borderColor);
};
// Sets the message in the center of the screen.
// leftMesage - optional
// rightMessage - optional
this.setMessage = function (message, leftMessage, rightMessage) {
this.barTopOfPage.find('.messageCell').html(message);
this.barTopOfPage.find('.leftMessage').html(leftMessage);
this.barTopOfPage.find('.rightMessage').html(rightMessage);
this.barTopOfScreen.find('.messageCell').html(message);
this.barTopOfScreen.find('.leftMessage').html(leftMessage);
this.barTopOfScreen.find('.rightMessage').html(rightMessage);
};
this.show = function() {
this.barTopOfPage.slideDown(1000);
this.barTopOfScreen.slideDown(1000);
};
this.hide = function () {
this.barTopOfPage.slideUp(1000);
this.barTopOfScreen.slideUp(1000);
};
var self = this;
if (hideAfterMS != undefined) {
setTimeout(function () { self.hide(); }, hideAfterMS);
}
}
To use it you must use jQuery and ensure there are no margins or padding on the body of your page.
The parameter that the AdvancedMessageBar takes is optional. If provided it will cause the bar to disappear after a certain amount of time in milliseconds.
var mBar = new AdvancedMessageBar(10000);
mBar.setMessage('This is my message', 'Left Message', 'Right Message');
mBar.show();
If you want to stack these then just create more AdvancedMessageBar objects and they'll automatically stack.

Related

How do I insert a link in the editor?

I'd like to add a hyperlink in a editor, like vscode does:
I'd like to add this formatted document and when you click into it, some operation happens, open a file dialog, for example.
I have no code to show yet because I didn't find anything like that yet, only for regular text that goes like this:
const line = editor.getPosition();
if(!line) {
throw new Error('line is null');
}
const range = new monaco.Range(line.lineNumber, 1,
line.lineNumber, 1);
const text = "empty tab";
const op: monaco.editor.IIdentifiedSingleEditOperation = {
range: range,
text: text,
forceMoveMarkers: true
};
editor.executeEdits('my-source', [op]);
but I didn't see how add a format it.
You can use an overlay element and define the placeholder content in HTML, with links that will perform actions (e.g. change the editor theme, change the language etc).
The HTML for the placeholder would look something like this:
<div class="monaco-placeholder">
This is a test placeholder that will disappear when you click into the editor.
Click
here
first if you want to change the editor language from HTML to JavaScript, or click
here
if you want to change the editor theme
</div>
Along with the following CSS:
.monaco-placeholder {
color: darkturquoise;
display: none;
position: absolute;
top: 0;
left: 65px;
pointer-events: all;
z-index: 1;
opacity: 0.7;
}
You can then wire this up in JavaScript as follows:
Functions to hide and show the placeholder:
function showPlaceholder() {
document.querySelector(".monaco-placeholder").style.display = "initial";
}
function hidePlaceholder() {
document.querySelector(".monaco-placeholder").style.display = "none";
}
Create the editor and show the placeholder:
const instance = monaco.editor.create(document.getElementById('container'), {
value: "",
language: 'html'
});
showPlaceholder();
Add event handlers for any links in the placeholder that you want to perform actions when clicked:
document.getElementsByClassName('change-language')[0].addEventListener('click', (e) => {
e.stopPropagation();
var model = instance.getModel();
monaco.editor.setModelLanguage(model, "javascript")
console.log('language successfully changed to JavaScript')
});
document.getElementsByClassName('change-theme')[0].addEventListener('click', (e) => {
e.stopPropagation();
monaco.editor.setTheme('vs-dark')
console.log('theme successfully changed')
});
Event handler to clear the placeholder and focus into the editor when the user clicks on any part of the placeholder apart from the links:
document.getElementsByClassName('monaco-placeholder')[0].addEventListener('click', () => {
hidePlaceholder();
instance.focus();
});
If you copy the HTML, CSS and JavaScript below into the Monaco Playground, you will see this working:
HTML
<div id="container" style="height: 100%"></div>
<div class="monaco-placeholder">
This is a test placeholder that will disappear when you click into the editor.
Click
here
first if you want to change the editor language from HTML to JavaScript, or click
here
if you want to change the editor theme
</div>
CSS
.monaco-placeholder {
color: darkturquoise;
display: none;
position: absolute;
top: 0;
left: 65px;
pointer-events: all;
z-index: 1;
opacity: 0.7;
}
JavaScript
const instance = monaco.editor.create(document.getElementById('container'), {
value: "",
language: 'html'
});
showPlaceholder();
function showPlaceholder() {
document.querySelector(".monaco-placeholder").style.display = "initial";
}
function hidePlaceholder() {
document.querySelector(".monaco-placeholder").style.display = "none";
}
document.getElementsByClassName('monaco-placeholder')[0].addEventListener('click', () => {
hidePlaceholder();
instance.focus();
});
document.getElementsByClassName('change-language')[0].addEventListener('click', (e) => {
e.stopPropagation();
var model = instance.getModel();
monaco.editor.setModelLanguage(model, "javascript")
console.log('language successfully changed to JavaScript')
});
document.getElementsByClassName('change-theme')[0].addEventListener('click', (e) => {
e.stopPropagation();
monaco.editor.setTheme('vs-dark')
console.log('theme successfully changed')
});

removeEventListener from another array-prototype forEach

Hello, friends!
I'm new to Javascript. Using native JS.
I need when I click on the red button the blue button becomes disabled using removeEventListener. And vice versa - clicking on the blue button will add removeEventListener to the red button.
But my method does not work because the first array does not see the other array.
Thanks for the help. And, please, add comments to your code))
Here is the code and example https://jsfiddle.net/of83ycmx/
<body>
<button class="red">Red</button>
<button class="blue">Blue</button>
<div class="box">BOX</div>
<button class="red">Red</button>
<button class="blue">Blue</button>
<div class="box">BOX</div>
<script>
const box = document.querySelectorAll('.box');
const red = document.querySelectorAll('.red');
const blue = document.querySelectorAll('.blue');
red.forEach((item, i) => {
item.addEventListener('click', function redListener() {
box[i].classList.add('redBox');
//removeEventListener doesnt work because blueListener is not defined
//item.removeEventListener('click', blueListener);
});
});
blue.forEach((item, i) => {
item.addEventListener('click', function blueListener() {
box[i].classList.add('blueBox');
// item.removeEventListener('click', redListener)
});
});
</script>
made some changes that made sense to me. I am quite a beginner, so anyone who sees anything no-no or bad practice go ahead and call me out.
What should happen:
My interpretation of your explanation is this: When a user clicks on 1 of 2 buttons, the button that was NOT clicked should have its event listener removed.
Solution:
Making the functions accessible was your main problem-named functions inside of event listeners are limited to that listener (I assume). So instead move the function outside of the listener and simply call the function:
function blueListener () {
// Do stuff here
}
item.addEventListener('click', blueListener)
Now the function is accessible to the other function, so when you remove the event listener you wont get blueListener is not defined.
By wrapping the buttons and the box in a div allows you to select the button you need. Using .querySelectorAll() on the parent div allows you to select the button with the respective class (i. e the selecting the blue button when you click the red button).
The functions don't need any other info; they use this to access the clicked element. Then you can find the parent element, and select the box to change the background color, and select the respective button to remove the event listener.
DEMO:
const box = document.querySelectorAll('.box');
const red = document.querySelectorAll('.red');
const blue = document.querySelectorAll('.blue');
function redListener() {
var parent = this.parentElement;
var box = parent.querySelectorAll('.box')[0]
var blueButton = parent.querySelectorAll('.blue')[0]
box.classList.add('redBox');
blueButton.removeEventListener('click', blueListener)
}
function blueListener() {
var parent = this.parentElement;
var box = parent.querySelectorAll('.box')[0]
var redButton = parent.querySelectorAll('.red')[0]
box.classList.add('blueBox');
redButton.removeEventListener('click', redListener)
}
red.forEach((item, i) => {
item.addEventListener('click', redListener)
})
blue.forEach((item, i) => {
item.addEventListener('click', blueListener)
});
body {
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;
}
.box {
width: 100px;
height: 100px;
background-color: rgb(172, 172, 172);
margin: 0px 0px 40px 0px;
display: flex;
justify-content: center;
align-items: center;
}
.redBox {
background-color: rgb(156, 56, 56);
}
.blueBox {
background-color: rgb(66, 56, 156);
}
<div class="buttons">
<button class="red">Red</button>
<button class="blue">Blue</button>
<div class="box">BOX</div>
</div>
<div class="buttons">
<button class="red">Red</button>
<button class="blue">Blue</button>
<div class="box">BOX</div>
</div>

Open app in App Store if App is not installed in Device by Deep Linking in iOS [duplicate]

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.

jQuery Mobile - Slide In Alert Bar CSS over Header

I am trying to make an alert bar slide in over my header bar in jQuery mobile. So far I have got the slide in down, but I am having trouble with the CSS. I originally tried make the outer most div with position: absolute; top 0px: which makes it slide over the header from the top, but then inside Safari on the iPhone, the close button is cut off and you have to scroll to the right. How do I fix that?
Here is the HTML code for the alert bar:
<div class="ui-bar ui-bar-b error" style="position: absolute; top: 0px;">
<h3>
Form Validation Errors
</h3>
<div style="display:inline-block; width:8%; margin-top:0px; float: right;">
Dismiss
</div>
<ul class="validation_errors_list"></ul>
</div>
I ended up finally use this CSS. The alert bar slides directly over the header.
//you only really need this just to get it to slide over the header nicely and make sure you use top:0 if you always want it to be at the top. The plugin I made shows in/out the error message at position you are scrolled to in the document
.alert{
position: absolute;
z-index: 9998;
width: 100%;
height: 30px;
display: none;
color: #ffffff;
text-shadow: none;
font-family: Helvetica,Arial,sans-serif;
}
//This CSS is only used if you have an X button to close the alert. See the plugin below.
.alert-button-container{
display:inline-block;
margin-top:-10px;
margin-right: 15px;
float: right;
}
Here is my HTML Code (note the ui-bar class is a jQuery mobile class that you need to add so you don't have to mess around some of the width and sizing stuff).
<div class="ui-bar alert">
<div class="alert-message"></div>
</div>
Here is a custom plugin I made from jQuery to do this alert bar.
Features + Use Cases
Features: Fades In/Out gracefully, can inject custom HTML error messages, can render a list of messages, slides over header, has a close X button for error messages, works on all browsers that I have tested so far (IE, iOS, Firefox), error messages appear at the position you are scrolled to in the document. No more have to scroll up to see the error :)
Form Validation Errors. You can pass in an array of error messages and it will parse it into a list.
var messages = new Array();
messages[0] = 'My Message';
//prevent from showing accidentally
if(messages.length > 0)
{
$(".alert").alertBar('error', "<h2>Form Validation Errors</h2>", {
'errorMessages': messages
});
}
Success or action messages:
$(".alert").alertBar('success', 'Removed From Your Itinerary');
////////////plugin code
(
function($) {
$.fn.alertBar = function(alertType, alertMessage, userOptions) { //Add the function
var options = $.extend({}, $.fn.alertBar.defaultOptions, userOptions);
var $this = $(this);
$this.addClass(options.cssClass)
.empty()
.html(alertMessage)
.css('top', $(document).scrollTop());
if(alertType == 'success')
{
$this
.fadeIn()
.addClass('alert-success')
.delay(options.animationDelay)
.fadeOut();
}
if(alertType == 'error')
{
var button = $('<div>')
.addClass('alert-button-container')
.append(
$('<a>').attr({
'href': '#',
'data-role': 'button',
'data-icon': 'delete',
'data-iconpos': 'notext',
'class': 'dismiss-error'
})
.append('Dismiss')
);
//build error container
$this
.addClass('alert-error')
.append(button);
//add optional items to error container
if(options.errorMessages)
{
var $messageList = $('<ul>').addClass('error-message-list');
for ( var i=0, len=options.errorMessages.length; i<len; ++i ){
$messageList.append(
$('<li>')
.append(options.errorMessages[i])
);
}
$this.append($messageList);
}
//show alert bar
$this
.trigger('create')
.fadeIn();
$(".dismiss-error").live('click', function(){
$this.fadeOut();
});
}
if(alertType == 'informational')
{
$this
.addClass('alert-informational')
.fadeIn()
.delay(options.animationDelay)
.fadeOut();
}
return $this;
};
$.fn.alertBar.defaultOptions = {
cssClass : 'alert',
alertBarType: '',
animationDelay: 1500
};
})(jQuery);
additional CSS classes if you use this. It just changes the color of the bar.
.alert-success{
background-color: #8cc63f;
}
.alert-error{
background-color: #ed1c24;
height: auto;
}
.alert-informational{
background-color: #0071bc;
}
Example picture:

jQuery plugin for Facebook "Like" Button

On lots of sites now, you can see a Facebook "Like" Button.
- When depressed, it changes background color.
- When mouse-overed, it allows you to write some additional text
I love this interface - lightweight action, but allow for expression of more data if the user wants to.
Anyone has written a similar plugin?
UPDATE:
See: http://www.engadget.com/2010/05/30/htc-evo-4g-gets-hacked-froyo-port-sense-ui-be-damned/ at the bottom of a post, you will see the facebook like button
I don't know of such a plugin for jQuery, but writing the user-interface is quite simple.
(Edit: Actually I just thought of a place where I could use this feature myself. I might just as well write a proper plugin based on this next week if I have the time, and edit it here. For the time being, below is what I originally posted...)
All you need is a couple of divs:
<div id="thebutton">Click me!</div>
<div id="thebox" style="display:none;">Content goes here</div>
And some jQuery:
<script type="text/javascript">
$(function () {
$('#thebutton')
.click(function () {
//Show/hide the box
$(this).toggleClass('activated');
$(this).hasClass('activated') ? $('#thebox').fadeIn() : $('#thebox').fadeOut();
})
.mouseenter(function () {
//If the button is .activated, cancel any delayed hide and display the box
$(this).addClass('hovering');
if ($(this).hasClass('activated')) {
$('#thebox').clearQueue().fadeIn();
}
})
.mouseleave(function () {
//Hide the box after 300 milliseconds (unless someone cancels the action)
$(this).removeClass('hovering');
$('#thebox').delay(300).fadeOut();
});
$('#thebox')
//When hovering onto the box, cancel any delayed hide operations
.mouseenter(function () { $(this).clearQueue(); })
//When hovering off from the box, wait for 300 milliseconds and hide the box (unless cancelled)
.mouseleave(function () { $(this).delay(300).fadeOut(); });
});
</script>
The rest is pretty much just CSS for #thebutton, #thebox, .hovering and .activated.
Here's a spartan look I used while writing this:
<style type="text/css">
#thebutton { width: 100px; background-color: #eee; text-align: center; padding: 10px; cursor: pointer; }
#thebutton.activated { font-weight: bold; }
#thebutton.hovering { color: Blue; }
#thebox { background-color: #eee; position:relative; width: 300px; height: 200px; padding: 10px; top: 5px; display: none;}
</style>
How about this jquery plugin: http://socialmediaautomat.com/jquery-fbjlike-js.php
It's really simple to set up and lets you perform some neat tasks in combination with the jquery cookie plugin (have a look at the demo page).
You can handle the hover, mousedown, and mouseup events and change the button's content or style.
Is not a plugin it uses the Facebook Javascript SDK. You load it by placing this at bottom of your document:
<div id="fb-root"></div>
<script>
window.fbAsyncInit = function() {
FB.init({status: true, cookie: true,
xfbml: true});
};
(function() {
var e = document.createElement('script'); e.async = true;
e.src = document.location.protocol +
'//connect.facebook.net/en_US/all.js';
document.getElementById('fb-root').appendChild(e);
}());
</script>
Add this attribute to your HTML tag(the actual HTML tag right after the DOCTYPE):
xmlns:fb="http://www.facebook.com/2008/fbml"
And then you can place this snippet wherever you want a Like button:
<fb:like></fb:like>
Using the $('#your-button').button(); function from the jQuery UI library gives this functionality, and a whole lot more.
http://jqueryui.com/themeroller/

Resources