This question already has answers here:
Warn user before leaving web page with unsaved changes
(17 answers)
Closed 9 years ago.
I am currently working on a website in aspx, where I need to detect and record if a user is about to leave the site.
It will need to capture if possible all of the following, if the user:
clicks on the browser Back or Forward buttons
clicks on the browser close button
clicks on the tab close button
navigates to a new website
If it's remotely possible, change the requirement, because it will lead to a less-than-ideal user experience.
On at least some browsers, you can do a synchronous ajax request from the onbeforeunload handler to send information to your server that the user is leaving the page:
window.onbeforeunload = function() {
$.ajax({
url: "/path/to/page",
data: {/* ...stuff... */,
async: false // <== Option is being removed soon
});
};
The reason I say it's a bad user experience is that since the ajax call is synchronous, it holds them up, and fairly intrusively on some browsers (the whole thing locks up while waiting for the request to complete). This is also why it may well not be reliable cross-browser.
The jQuery team is removing the async option from ajax at some point. It's still there in the underlying XMLHttpRequest object, so when that happens you can use that directly.
The fact the user left the site is already apparent from the web server logs, although you can't tell how long they spent on the final page. If that "how long did they spend on the final page" is really vital information, rather than holding them up when they leave (and relying on something that may not be entirely reliable cross-browser), you could use a background "ping" while they're still on the page. It would probably be best to do the pings further and further apart over time.
So for instance, when the page loads:
(function($) {
var SECOND = 1000;
var MINUTE = 60000;
var arrived = +new Date();
var pingTimes = {
0: 10 * SECOND, // Every 10 seconds in first minute
1: 30 * SECOND, // Every 30 seconds in second minute
2: 45 * SECOND, // Every 45 seconds in third minute
other: 60 * SECOND, // Every minute otherwise
long: 10 * MINUTE // Every 10 minutes if they've been here a long time
};
nextPing();
function ping() {
$.ajax({
url: "/path/to/ping/page",
method: "POST",
success: nextPing,
error: nextPing
});
}
function nextPing() {
var elapsed, pingTime;
// Get the # of full minutes they've been here
elapsed = Math.floor((new Date() - arrived) / MINUTE);
// If it's been a long time, use `pingTimes.long`.
// Otherwise, use the time from the table or the default.
pingTime = elapsed > 15 * MINUTE ? pingTimes.long : (pingTimes[elapsed] || pingTimes.other);
setTimeout(ping, pingTime);
}
})(jQuery);
Related
I want to update status flag 1 if user working on say 'XYZ' page and immediately update 0 when user leaves this page
i can able to update flag 1 by simple update query with linq in respective controller when user enters in.
but the problem is how to update flag 0 when user leaves or closes browser window directly
i tried adding following code :
window.onload = function () {
window.addEventListener("beforeunload", function (e) {
----------AJAX FUNCTION TO UPDATE DATA HERE--------------
});
};
But the problem is how to get callback of this alert-box
see the image here
You can't be certain that this will work consistently just by using clientside functionality, as there could be many ways a session ends, which would NOT be caught by this. Examples could be:
losing internet connection
losing power
server endpoint being down
If you want consistency in this, you have to back it up with something server-side, that "expires" sessions that have not been heard from in x minutes.
So my suggestion:
Set flag to 1 when user enters page.
Implement the code you already have to update flag to 0.
Run a server function (using hangfire for background jobs could be an option) and update all sessions with flag 1 to 0 when it has not been active for x minutes.
I am using the scrollTop function to detect how close a user is from the bottom of the screen to add more content. My javascript function is like this:
if ($(window).scrollTop() >= $(document).height() - $(window).height() - 75) {
var url = '<%=Url.Action("MoreView", "Status", new { id = ViewData["Id"],
page = Convert.ToInt32(ViewData["PageNumber"])+1 })%>';
NextPage(url); //this does an ajax call to the action method on the server
}
Everything is working fine, the problem is that the window.ScrollTop >75 is happeng sometimes 2 or 3 times so fast that it loads the same content 2 or 3 times. I can probably fix this by using a session variable on the server side but didn't want to go that route because that can get ugly IMO. Any suggestions?
As it stands right now you will always be making a request to the same URL (resulting in the same data every time, unless I mis-understand what you are doing). Once you fix that, you'll probably want to wrap your function that is called when the user scrolls in a debounce function (I'm using Underscore.js' debounce function for this example):
// Only call yourOnScroll once it hasn't been called
// for more than 100 milliseconds
yourOnScroll = _.debounce(youOnScroll, 100);
I was wondering if anyone knows a good way to regulate how many emails are sent through C#?
Here is my scenario. I have a windows service that monitors three other windows services. I am using a service controller to get the status of all 3 services and if the status of any of these services change to stopped, it sends an email. My issue is, I run this on a 60 second timer so it sends an email every 60 seconds until someone starts the service back up.
My first thought was, when first email is sent, create a text file and use a counter to number it. Do this while counter < 6 so I will only receive 5 emails max. I think this will work but it seems kind of silly.
Does anyone have an alternative to this or should I just go with my first thought and perform clean up on the files?
Thank you
EDIT: The reason that I was trying to limit the number of emails sent is because the people who receive these emails do not react very quickly. At the same time, those who handle Exchange do not want the service to spam people. I felt 5 would be enough to appease both sides.
I would suggest that you should track the down time of each service.
So every 60 seconds you check, if a service is down, store the DateTime that the service is down. The on the next 60 second interval you can check to see if the service was already down. i.e. you can tell if the service just went down or has been down a while. You can also add another flag to determine if the the last check was UP or DOWN.
Then when the program first finds the service down it can send the email. Once the service is back up it can reset this flag values so the next down time it knows to send a new email.
You can then also use these flags to delay email frequency if desired. Just add a new DateTime field for LastEmailSentTime and compare that with whatever interval you want for error emails (say 10 minutes)
Hope that gives you some ideas
EDIT: Some Sample...
bool ServiceWasDown = false;
DateTime ServiceDownTime = DateTime.Now;
DateTime LastEmailTime = DateTime.Now;
void OnTimerElapsed()
{
if(IsServiceDown())
ServiceDown();
else
ServiceUp();
}
void ServiceDown()
{
if(ServiceWasDown)//already know about service
{
//if LastEmailTime more than 10 minutes ago send another email and update LastEmailTime
}
else//service just went down
{
//send new email
LastEmailTime = DateTime.Now;
ServiceWasDown = true;
ServiceDownTime = DateTime.Now;
}
}
void ServiceUp()
{
ServiceWasDown = false;
}
If you use a System.Timers.Timer then You can add a int variable for count Elapsed events.
I am using MVC 2, I have view which simply displays label with current time on it.
I want to update this View(label) every 5 seconds so time will update. I am using below (taken from here) but doesn't seem to be working.
public ActionResult Time()
{
var waitHandle = new AutoResetEvent(false);
ThreadPool.RegisterWaitForSingleObject(
waitHandle,
// Method to execute
(state, timeout) =>
{
// TODO: implement the functionality you want to be executed
// on every 5 seconds here
// Important Remark: This method runs on a worker thread drawn
// from the thread pool which is also used to service requests
// so make sure that this method returns as fast as possible or
// you will be jeopardizing worker threads which could be catastrophic
// in a web application. Make sure you don't sleep here and if you were
// to perform some I/O intensive operation make sure you use asynchronous
// API and IO completion ports for increased scalability
ViewData["Time"] = "Current time is: " + DateTime.Now.ToLongTimeString();
},
// optional state object to pass to the method
null,
// Execute the method after 5 seconds
TimeSpan.FromSeconds(5),
// Set this to false to execute it repeatedly every 5 seconds
false
);
return View();
}
Thanks for help in advance!
What you are doing won't work as once the initial response is sent to the client, the client will no longer be listening for data from your server for that request. What you want to do is have the client initiate a new request every 5 seconds, then simply return the data for each request. One way to do this is with a refresh header.
public ActionResult Time()
{
this.HttpContext.Response.AddHeader( "refresh", "5; url=" + Url.Action("time") );
return View();
}
You need to put your recurring loop on the client side so that it reloads the page every five seconds.
One way, using Javascript:
<script>setTimeout("window.location.reload();",5000);</script>
The code you provided, runs at server, and when a page (View in this case) is sent to the client, the server will forgot it! You should create a client side code for refreshing the page every 5 seconds. You can use a header command (refresh) or a script:
<script>
setTimeout("window.location.reload();", /* time you want to refresh in milliseconds */ 5000);
</script>
But if you just want to refresh the page for updating the Time, I never suggest you to refresh page completely. Instead, you can create a javascript function, to tick every 5 seconds, and calculate the current time and update the label.
I am attempting to improve the stability of the web dashboard I have created. I have noticed that the main cause of getting into a bad state is when the user moves too quickly and requests get cut short.
For instance, I have the following javascript:
//When a resize occurs a lot of handling needs to happen to convert the user's action into
//a visibly-pleasing result. In addition, the server has to be spoken with in order to save the controls.
function OnClientResizing(pane, eventArgs) {
eventArgs.set_cancel(true);
var parameters = new Array();
parameters.push("Resize");
parameters.push(pane.get_id());
parameters.push(eventArgs.get_delta());
__doPostBack(pane.get_splitter()._uniqueID, parameters);
}
This function passes the hard work back to the server, so that it can calculate the appropriate ways to resize the controls on the page during resizes. This takes X seconds. If the user then resizes the page again before X seconds has elapsed -- I enter into a bad state. Either the old request gets cut off prematurely, or the new one runs at the same time. Either way, controls become mishapen on the page.
As such, I would like to queue future resizes, or play around with canceling current requests. I read that the best way to do something like this is to simply set a flag outside the scope of this function. I can do that, but I am not sure how to detect the end of a doPostBack. Am I supposed to change the javascript variable from the server-side somehow in PageRequestManager - EndRequest?
Cheers
First off, don't let your server participate in UI resize algorithms. Do that entirely client side. You can send resulting data to the server at any time, but don't make a real-time UI positioning depend upon a server response. That should be handled client-side with CSS or javascript logic.
Second off, if your code can't handle two ajax calls in flight at the same time, then your options are as follows:
Fix your code so it can handle sequential ajax responses in flight at the same time.
Cancel/ignore the first ajax response the moment you send a second one so that you ignore the response from the first and wait for the response from the second.
Prevent a second ajax request until the first one completes. I wouldn't suggest queueing them because that's just going to lead to an even worse user experience.
The details of how to do 1, 2 or 3 depend upon how your code works which you have not yet shared.
The easiest is option 3). That can be done with just a global flag. Just define a global variable, set it to true when you start an ajax call and clear it when the ajax call completes (in a completion function):
var ajaxInFlight = false; // global declaration
function OnClientResizing(pane, eventArgs) {
if (ajaxInFlight) return; // do nothing if ajax call already in flight
ajaxInFlight = true;
eventArgs.set_cancel(true);
var parameters = new Array();
parameters.push("Resize");
parameters.push(pane.get_id());
parameters.push(eventArgs.get_delta());
__doPostBack(pane.get_splitter()._uniqueID, parameters);
}
function postBackCompletionHandler(id, parms) {
ajaxInFlight = false; // clear global flag, ajax call done
// ... rest of your function here
}
You will also have to make sure that error conditions are handled so that the global flag is reset if the ajax call fails for any reason.