I have
<span class="cssButton"> <img src="http://uber-upload.com/img/icons/up_16.png" alt=""/> Uber-Upload! </span>
And i want to make it so that if you press that button, it also sets a variable that makes it so that if you try to leave the page, it will pop up one of those "Are you sure you want to leave this page" alerts to prevent people from accidently leaving while there is an upload going on.
Dont worry about unsetting the variable after the upload finishes, i'll add that, i just need one of you smart coders to make me a framework.
Thanks!
Add this declaration to the page:
var upcount = 0;
Change your onclick to:
onclick="++upcount; swfu.startUpload();"
If the swfu gives you some kind of event when it's done uploading, put this on it:
--upcount;
And add this handler:
window.onbeforeunload = function() {
if (upcount > 0) {
return "The upload is still in progress, leaving the page will cancel it.";
}
}
Where browsers respect the onbeforeunload, that'll work. And most of them do, most of the time. Where they don't, well, you tried. Note that by using a counter, you're allowing for multiple uploads.
You might consider attachEvent (IE) or addEventListener (standard) (or a library like Prototype or jQuery that evens out the differences for you) for attaching your events rather than using attributes. Using attributes (aka DOM0 handlers) is fairly old-fashioned and a bit limiting (only one handler per event per element). But it does still work.
function warnUser()
{
if(dataIsDirty)
{
return "You have made changes. They will be lost if you continue.";
}
else
{
// Reset the flag to its default value.
dataIsDirty = false;
}
}
function isDirty()
{
dataIsDirty = true;
}
function isClean()
{
dataIsDirty = false;
}
Then you use it just like on the onbeforeunload event.
window.onbeforeunload = warnUser;
dataIsDirty = false; //Or true, depending on if you want it to show up even if they dont' make changes)
Then to use it, just use the 'isClean' function to anything you don't want to trigger it(Save, for instance), or you can attach 'isDirty' to any event you want to trigger it before they leave (say, editing a form field).
Relying on onbeforeunload is sketchy at best. Because spammers have abused the behavior in the same way you're suggesting the ability for people to do this has been basically removed.
You can now only respond to onbeforeunload if the close event was fired from activating a button or such.
Related
My intent is to throttle the click listener on some links and form submit buttons. The main idea was something like:
Click
<script>
window.onload = function() {
tags = document.findElementsByClassName("throttled-click");
for (let tag of tags) {
tag.onclick = _.throttle(tag.click, 1000, { 'trailing': false });
// Clearly doesn't work
}
}
</script>
The code above doesn't really work since no matter what I do, the default click event listener won't get throttled. If I pass in some other function (e.g. console.log("Throttled")), it will be throttled but the default click event listener won't.
Other than attempting to write my own throttling function, I'm out of ideas.
Note that I'm not a js dev so I may be missing something obvious.
EDIT: The goal of throttling the default click event listener is to prevent users from submitting too many forms when something hangs. Granted, form submissions usually entail a redirection which implicates that it's enough to simply disable the HTML click event after the first click.
My idea was to implement a throttle for cases when the page won't refresh or some edge case occurs where the request never reaches the server and the user actually has to click the submit button again.
I was able to do it with a custom implementation, I don't think there's a way to do it with existing standard libraries which I find kind of strange.
I have a Chrome extension that intercepts and checks tweets before they get posted. To do this, I've add an event listener to the Tweet button. Sine the content is dynamic, I use the solution proposed in this thread:
initialize : function() {
let that = this;
let jsInitChecktimer = setInterval(checkForJsFinished, 111);
function checkForJsFinished () {
if (document.querySelector("div[data-testid='tweetButtonInline']")) {
clearInterval (jsInitChecktimer);
console.log("Button found");
that.addSubmitNewTweetClickHandler();
}
}
},
addSubmitNewTweetClickHandler : function() {
let that = this;
let buttonSubmitTweet = document.querySelector("div[data-testid='tweetButtonInline']");
buttonSubmitTweet.addEventListener('click', function(e) {
console.log("CLICK");
// Stop default event from happening
e.preventDefault();
e.stopImmediatePropagation();
// Do stuff
});
},
If the tweet passed the checks alright, it gets submitted by programmatically triggering the event using .trigger('click').
This works fine, but only once. After a tweet has been submitted and posted, the event listener on the Tweet button is gone, and I cannot intercept the next tweet to check it. I've tried calling initialize() after submitted again -- maybe the button gets removed and newly added to the DOM (it actually disappears fire a moment when submitting a tweet) -- but the querySelector finds the button immediately. But even after calling initialize() again, no click even on the Tweet button fires.
What could be the issue here? My problem is that I don't even know where to look for and how to debug this.
After many more hours, I've finally figured it out. The problem was essentially the highly dynamic content of the new Twitter website. After submitting a tweet, the Tweet button gets indeed removed and added again. In needed to do a serious of changes:
Use a MutationObserver to keep track of any changes. Every time there's a change, call the initialize() function. To avoid too many calls, I do this in case of certain changes (unnecessary detail here)
Change the addSubmitNewTweetClickHandler() method so that the event listener first gets removed in order to avoid duplicate listeners (please note that I use objects hence the use of this compared to my original question)
addSubmitNewTweetClickHandler : function() {
let that = this;
let buttonSubmitTweet = document.querySelector("div[data-testid='tweetButtonInline']");
buttonSubmitTweet.removeEventListener('click', this.handleSubmitNewTweetClick );
this.handleSubmitNewTweetClick = this.handleSubmitNewTweetClick.bind(this)
buttonSubmitTweet.addEventListener('click', this.handleSubmitNewTweetClick );
},
This change required to create the reference function handleSubmitNewTweetClick
Overall, it's still not a perfect solution since I call initialize() many unnecessary time. However, I failed to reliably identify when the Tweet button was added to the document. When I used the MutationObserver none of the added nodes had the attribute data-testid which I need to identify the correct button. I have node idea why this attribute was not there. Maybe the attribute is added some times after added to button, but even with an additional MutationObserver looking for attribute changes I could detect this.
Anyway, it works now and it's only for a prototype.
I have many different types of links, which have click event handlers. For example:
$('a.category-one').click(function() {});
$('a.category-two').click(function() {});
Now I want to add a permission check. When one clicks on a link, this check should decide whether to proceed with the other defined event handler. If the other event handler should not be invoked, then display a modal window to explain why the action on a link is stopped. If it should proceed, then just let it go ahead silently.
I have been struggling about what is the best way to do this with Javascript. I think about modifying each handler by adding the permission check, but feel it may be too intrusive and such a check is kind of cross-cutting. I think about adding a class to each of the links and attaching the permission check to such a class, but feel the sequence of events might not be what I expect, and not sure how to stop invoking the second handler from within the first handler.
I want to hear how experts out there say about this.
Thanks and regards.
I was also thinking you could add class permissions to the links, but if you add a more complex permission check this could get ugly. Still, it's a possibility and makes your javascript methods a little more readable.
What if rather than inline methods on the click events you rather call a wrapper method? It could also end up DRYing up some of the overlap functionality of the click events if there are any.
EDIT:
So for your click events you could do
$('a.category-one').click(eventAction($(this));
$('a.category-two').click(eventAction($(this));
function eventAction(control) {
// Permission check here
if control.hasClass('category-one') {
...
}
if control.hasClass('category-two') {
...
}
}
In my opinion, the simplest way to do this is adding a class like .permission-check to the links you want to check, and then use an onclick function like this, which prevents the default behavior if some condition happens:
$('a.permission-check').click(function(e) {
if ( /* no permission */ ) e.preventDefault();
});
On the other hand, if your elements have more handlers and you want to stop all the other handlers if the permission check fails you will need to create a permission check funcion and then use it inside every handler, like this:
function permissionCheck(element) {
// do something with the element
// then return true if it is ok, false if not
if ( /* some condition */ ) return true; // permission
return false; // no permission
}
$('a.category-x').click(function(e) {
if (!permissionCheck(this)) e.preventDefault();
// something else...
});
On a dynamic page I wish to disable and later re-enable sections. Visually they are grayed out, but need to prevent users from playing with the section until later.
For elements using on-click triggers, would like to:
save the current on-click trigger in an attribute
remove the current on-click trigger
add trigger that no-defaults and no-propagates
to re-enable:
get rid of the no-default trigger
re-apply the trigger previously saved
clear attribute where it was saved
From replies so far:
conjecture: using pure JavaScript html5 level, without delegation or some other external mechanism, it is impossible to extract the on-click trigger from an element.
Solution
was very tempted by delegations - and was defeated by not being able to prevent memory leaks. ended up doing it anyway, with a simple gc to do the job.
extends (add|remove)EventListener with (add|push|pop|remove)PopableEventListener, making code change trivial. allows me to push and pop listeners to any level - wonderful for form context changes other than merely enable/disable.
source: http://code.google.com/p/chess-spider/source/browse/http/scripts/popable.js
doc: http://code.google.com/p/chess-spider/wiki/PopableEventListener?ts=1303738300&updated=PopableEventListener
Editorial
Contrary to most I have seen, the ability to access listeners in the dom would be a significant benefit; besides being able to sanely disable re-enable, the ability to coerce them into a different scope would be incredibly useful.
The best way to handle hundreds of triggers is to use event delegation.
You then examine each click for if it matches your trigger. If it is the child of a disabled section you discard the click.
A jQuery implementation would be something like as follows:
$('.trigger', document.body).live('click', function(e) {
var target = $(e.target);
if (target.parents('.disabled').length > 0) {
// handle click
}
});
Obviously you can use other frameworks or plain JavaScript as suits you best.
I presume you are talking about adding and removing listeners. You can do that a number of ways, the simplest if you only have one listener for an event is to add it as a property of the element. To remove it, just assign null:
function sayHi() {
alert('hi');
}
// Add a listener
var someElement = document.getElementById('someElementID');
someElement.onclick = sayHi;
// Remove it
someElement.onclick = null;
If you need more than one listener for an event, you can use other schemes, such as addEventListener and attachEvent
Of course you can just track the state of the elements (say in a class or object), then the listener can respond based on the state.
What about classes? Something like
function act(e) {
var target = e.currentTarget;
if (target.className === 'active') {
//element active, disable it
target.className = 'disabled';
//other stuff
}
else if (target.className === 'disabled') {
//element disabled, enable it
target.className = 'active';
e.preventDefault();
//other stuff
}
}
elem.onclick = act;
You can also be brave and use data-* attributes (link)
Why not just save each onclick's enabled state into an array, then check that array each time it is called, if the state is false, just return without running anything, this could also help keep track of what to grey out.
Here is my idea: http://jsfiddle.net/mazzzzz/MXEjv/1/
It is a bit messy, but the two top functions are the important ones. The first will toggle (based on class), and the second will say if the element's onclick is enabled (again by class). Just make sure the objects have the same class, and one will effect the other, and vise versa. Alternately, you could just pass in the id, instead of using the class (like I did).
Hope that helps a bit.
I am trying to create a customisation to our existing software, which launches on this event. I need to be able to catch the window.onbeforeunload again once the user has made their choice.
I have tried wrapping the functions but can't seem to be able to overwrite the one which is loaded first. Does anyone have any ideas about how I can approach this, or force the newly assigned function to overwrite the old one.
You can not cancel the unload depending on which button the user presses, and you can not invoke this event manually. It's not even a standard event. Think of the vulnerabilities that could be used for malicious purposes if the event had the capabilities you want it to have.
This is about all you can do...
window.onbeforeunload = function() {
alert("one"); // First time, choose to stay on page
window.onbeforeunload = function() {
alert("two"); // Second time
}
}