Recursion with alertify.js leading to auto-closing alerts - javascript

I am trying to convert the following old school snippet to alertify.js 0.3.8:
window.doPrompt = function() {
var str;
do str = prompt("Enter your name");
while (str === "" && (alert("Can't be empty!") || true));
if (str) document.getElementsByTagName("body")[0].innerHTML += ("<pre>Your name is: " + str + "</pre>");
}
JSFiddle of the above
This is what I first tried:
window.doPrompt = function() {
alertify.prompt(
"Enter your name",
function(confirmed, str) {
if (confirmed) {
if (str.length === 0) {
alertify.alert(
"Can't be empty!",
function() { doPrompt(); }
);
} else {
document.getElementsByTagName("body")[0].innerHTML += ("<pre>Your name is: " + str + "</pre>");
}
}
}
);
}
JSFiddle of the above
It is not working as intended. For example, in Firefox 19, if you use the keyboard to submit the prompt empty, the "error" alert will only show for a very short time and will then disappear by itself, which is not how it worked before.
I tried breaking the recursion by inserting calls to window.setTimeout with the timeout set to 0 around each alertify call. It didn't help.
It is okay if you can recommend me an alternate JavaScript library with a similar API that I can use instead (that doesn't have this problem of course).

It doesn't seem to be a problem with alertify.js, but with Firefox. If you search bugs related to transitionend (used in the dialog animation, according to the sources) you'll see some potential candidates for the issue you're experiencing (in particular "transitionend event not fired when there are multiple transitions"). This is consistent with what you're experiencing - if you try to show a dialog while other is still in place (i.e. the previous dialog is still in the middle of a transition) then things break.
I can offer a workaround, though. It's not pretty, but gets the job done. But first, an unrelated problem I spotted in all browsers:
The library seems to become confused if you append stuff to the body; using a "target" div instead solves the issue:
<div id="target"></div>
...
document.getElementById("target").innerHTML += ("<pre>Your name is: " + str + "</pre>");
Both when showing an alert and when re-displaying the prompt, you should use setTimeout as you suggested. However, it's not enough for the timeout to be zero, since the problem here is on the transitionend. Set a value high enough for the previous dialog to finish hiding and the problem is fixed:
setTimeout(function() {
alertify.alert(
"Can't be empty!",
function() {
setTimeout(function() {
doPrompt();
}, 500);
}
);
}, 500);
(if the value is not high enough, it will not only keep broken on Firefox but will also break once-working browsers like Chrome, so keep that in mind)
Working example. Tested successfully in Firefox 19.0, Chrome 25 and Safari 4.0.4.

Related

Using location.reload on Chrome is not working as it should, using the same code in Firefox works fine

I am making a Snake game using Javascript and checking if the user has touched the borders, if he has than it's gameover and a game over function is called which shows a confirm box with it's content based on the language of the html file because i have 2 html files: one for French and one for English.
this.alert = alert;
this.gameOver = function() {
const langCheck = document.getElementById("html").lang;
if (langCheck === "fr") {
alert = confirm("\t\tPerdu!\n\n Votre longueur était: " + this.eaten);
} else {
alert = confirm(
"\t\tGame Over!\n\n Your snake length was: " + this.eaten
);
}
console.log(alert);
if (alert === true) {
document.location.reload(true);
} else {
document.location.reload(true);
}
};
In chrome when i click either Ok or Cancel the confirm box just reopens instantly and it doesn't reload (Edit: Forgot to mention but the page just hangs after clicking ok) even though console logging alert it returns true. So why isn't this working on Chrome when on Firefox it's working perfectly?
I tried many different location.reload i saw when i researched on it like window.location.reload() but it's the exact same.
I'm stuck here because as i said in Firefox it works flawlessly and that's where i initially tested when i programmed the game but when i finished i decided to take a look if on Chrome it worked, that's when i found out.
Instead of location.reload() you can try (code taken from another solution online)
setTimeout(function(){
window.location.reload();
},100);
Also considering you're running the same code if alert === true and if it's not true, I would just get rid of the if/else and run the code directly (if that's what you intend to do)
document.location.reload(true); will be ok for Google chrome you can check the fiddle example here.
You have other alternatives also
setTimeout(function(){ window.location.reload(); });
window.location.reload();
A short note :
if (alert === true) {
document.location.reload(true);
} else {
document.location.reload(true);
}
Irrespective of result you are doing the same operation here, So you can simplify the code like below. The if else is not at all needed there.
document.location.reload(true);
it should be
window.location.reload(false);

JQuery submit function working sometimes

I'm making a web application for a class which contains a jsp file that uses jquery. I want the app to trigger an alert right before submitting. This works some of the time in my real program, and other times the alert is never triggered. I can't seem to peg down an instance where it always works or never works. My error console is silent on the matter, unless I add Firebug breakpoints. Then it gives me
Error: attempt to run compile-and-go script on a cleared scope
Source File: http://code.jquery.com/jquery-1.7.2.min.js
Line: 2
But I have no idea what that means. As far as Firebug goes, I can't understand why the evaulation stops before the alert message.
I tried to make an sscce documenting the problem, but I guess it was too different from my real program, because submit never worked. I'll show some code from my real program. (Sorry about the lack of SSCCE.)
$(document).ready(function() {
$("#questionDisplay").submit(function() {
var correctAnswer = $(".correctAnswer").attr("value");
var answer = "";
if ($(".multipleChoice").length > 0) {
answers = $("input:checked");
for (var obj in answers) {
answer += obj.attr("value") + "#";
}
} else if ($(".fillBlank").length > 0) {
for (var answerNo = 1; $(".answer" + answerNo).length > 0; ++answerNo) {
answer += $(".answer" + answerNo).attr("value") + "#";
}
} else {
answer = $(".answer1").attr("value");
}
if (answer == correctAnswer) {
alert("Yes! Correct!");
} else {
alert("Sorry, incorrect.");
}
});
});
The jsp is a huge mess (we have to use scriplets :( ), but if you'd like to see it just lemme know.
How do I get my submit handler to work every time?
In your example, answers is a jQuery collection of elements. Looping through it using for(var obj in answers) { } is actually looping through the properties of answers, not the elements themselves. Therefore, calling .attr() on a property is not going to work.
In general, if I see my debugger state that jQuery has an error, it's 99.9% of the time me calling a jQuery method on a non-jquery selected object. In this case, I saw the error in Chrome's JavaScript console, and sometimes results may vary with different consoles.
A good practice is to prefix variables that store jQuery elements with $ to indicate that they are jQuery objects. For instance, $answers makes it easier to keep track of what it contains.
Use:
answers.each(function() {
answer += $(this).attr("value") + "#";
});
Instead of this:
for (var obj in answers) {
answer += obj.attr("value") + "#";
}

SYNTAX_ERR: DOM Exception 12 - Hmmm

I have been working on a small slideshow / public display for a client that uses HTML5 Rock's Slideshow code. I have run into a DOM Exception 12 - a syntax error that is supposedly related to CSS selectors - while monkeying around with it... but I can't trace it back to any changes I made in the code. I am thinking it might be something that was uncovered as I added features.
I have traced it down to this object (live version here):
var SlideShow = function(slides) {
this._slides = (slides || []).map(function(el, idx) {
return new Slide(el, idx);
});
var h = window.location.hash;
try {
this.current = h;
} catch (e) { /* squeltch */ }
this.current = (!this.current) ? "landing-slide" : this.current.replace('#', '');
if (!query('#' + this.current)) {
// if this happens is very likely that someone is coming from
// a link with the old permalink format, i.e. #slide24
alert('The format of the permalinks have recently changed. If you are coming ' +
'here from an old external link it\'s very likely you will land to the wrong slide');
this.current = "landing-slide";
}
var _t = this;
doc.addEventListener('keydown',
function(e) { _t.handleKeys(e); }, false);
doc.addEventListener('touchstart',
function(e) { _t.handleTouchStart(e); }, false);
doc.addEventListener('touchend',
function(e) { _t.handleTouchEnd(e); }, false);
window.addEventListener('popstate',
function(e) { if (e.state) { _t.go(e.state, true); } }, false);
};
Instantiation of SlideShow() (line 521 in main.js):
var slideshow = new SlideShow(queryAll('.slide'));
Calling queryAll('.slide') returns an array of all the slides with an class of .slide. However, when passing queryAll('.slide') as a parameter for instantiating SlideShow(), it returns a DOM Exception 12 error.
Has anybody seen this before?
You are using illegal id-attributes(illegal before HTML5) inside the document, e.g. 2-slide . Fix them.
To explain:
to solve the known misbehaviour of element.querySelectorAll() the selector .slide will be internally rewritten(by using the id of the element). This will result in something like that:
#2-slide .moreselectors
...and forces the error, because an ID may not start with a Number.
See the fiddle: http://jsfiddle.net/doktormolle/FGWhk/
If you are coming here after searching for this error in HTML5 rocks slides:
For some reason they remove the class 'to-build' with the following:
toBuild[0].classList.remove('to-build', '');
That breaks all slide decks the use build, even the Google demo right now is broken
Just change line 220 of default.js to
toBuild[0].classList.remove('to-build');
all is well!
In my case it was using self.postMessage(e.data); in the main thread while using web workers.
I know it's not related to the OP's issue, but it's an odd error so I'm leaving this here in hope it helps others.
Same problem to me but in my case a try to get elements from their attribute
document.querySelectorAll('input[name="path"]')
and SYNTAX_ERR: DOM Exception 12 occurred only on Safari. So i've change it to get the element directly from class and now work fine.
You can escape the quotes like in applescript then no issue on safari
do JavaScript "document.querySelector('span[" & attrName & "=\"" & attrValue & "\"]').click();"

What is the difference between this.click() and $(this).click()?

In the end, I have decided that this isn't a problem that I particularly need to fix, however it bothers me that I don't understand why it is happening.
Basically, I have some checkboxes, and I only want the users to be able to select a certain number of them. I'm using the code below to achieve that effect.
$j( function () {
$j('input[type=checkbox].vote_item').click( function() {
var numLeft = (+$j('#vote_num').text());
console.log(numLeft);
if ( numLeft == 0 && this.checked ) {
alert('I\'m sorry, you have already voted for the number of items that you are allowed to vote for.');
return false;
} else {
if ( this.checked == true ) {
$j('#vote_num').html(numLeft-1);
} else {
$j('#vote_num').html(numLeft+1);
}
}
});
});
And when I was testing it, I noticed that if I used:
$j('input[type=checkbox]').each( function () {
this.click()
});
The JavaScript reacted as I would expect, however when used with:
$j('input[type=checkbox]').each( function () {
$j(this).click()
});
It would actually make the counter count UP.
I do realize that it isn't the most secure way to keep count using the counter, however I do have server side error-checking that prevents more than the requisite amount from being entered in the database, that being the reason that I have decided that it doesn't actually need fixing.
Edit: The $j is due to the fact that I have to use jQuery in noConflict mode...
$(this) contains a jQuery wrapper (with lots of functions) whereas this is solely the DOM object.
The fact that counter is going up gave me the clue that there is a link between checked attribute, which you are using, and firing the click event manually.
I searched Google for 'jquery checkbox click event raise' and found this link, where author faces the exact same problem and the workaround he used.
http://www.bennadel.com/blog/1525-jQuery-s-Event-Triggering-Order-Of-Default-Behavior-And-triggerHandler-.htm
On a side note, I think you can simplify your code further:
$j('input[type=checkbox].vote_item').click(
function()
{
var maxNumberOfChoices = 5;
//get number of checked checkboxes.
var currentCheckedCount = $j('input[type=checkbox].vote_item :checked');
if(currentCheckedCount > maxNumberOfChoices)
{
//It's useful if you show how many choices user can make. :)
alert('You can only select maximum ' + maxNumberOfChoices + ' checkboxes.');
return false;
}
return true;
});
this.click() calls the browser DOM method click().
$(this).click() calls the jQuery method click(), which does more than just call the browser method: see the implementation of the function trigger for details.

Javascript: how do I determine if a link targets the same domain as the page it resides on?

For the purposes of tracking non-HTML documents via google analytics, I need the mentioned algorithm. It should:
not hard-code the domain
ignore the protocol (i.e. http/https)
not worry about the presence/absence of "www" (any absolute links WILL prefix with "www" and all pages WILL be served via "www")
This is complicated by the fact that I need to access it via a function called from the IE-only 'attachEvent'.
UPDATE Sorry, I've worded this question really badly. The real problem is getting this to work via an event, since IE has its own made-up world of event handling. Take the following:
function add_event(obj) {
if (obj.addEventListener)
obj.addEventListener('click', track_file, true);
else if (obj.attachEvent)
obj.attachEvent("on" + 'click', track_file);
}
function track_file(obj) { }
It seems as if the "obj" in track_file is not the same across browsers - how can I refer to what was clicked in IE?
I would like to point out that, if you're on so.com, the following links are URLs within the same domain:
http://test.so.com
http://so.com/index
index
/index
#
/#
https://subdomain.so.com#hash
mail.google.com
mail.google.com/index.php?var=value#index
(it may seem odd, but the last two ones are valid: if you're on http://so.com, the last one would take you to http://so.com/mail.google.com/index.php?var=value, which is perfectly valid)
This doesn't really answer the question but I hope it will guide the rest of the answers. If there's anything else weird enough, feel free to add it.
This sounds like a comedy answer but in all seriousness it would be be advisable that you could also do something like:
$('a.external')
Certainly the regex comparison to your window.location is the programmatic answer.
The method of attachment is not the only way IE and W3 event listeners differ. For IE you must read window.event.srcElement; in W3 it's event.target where event is the parameter passed to the callback function.
If you don't need multiple event handlers on links, old-school DOM 0 event handlers are probably an easier way for you to approach this, allowing you to just us ‘this’ to get the object on any browser.
function bindtolinks() {
for (var i= document.links.length; i-->0;)
document.links.onclick= clicklink;
}
function clicklink() {
if (this.host==window.location.host) {
dosomething();
return true; // I'm an internal link. Follow me.
} else {
dosomethingelse();
return false; // I'm an external link. Don't follow, only do something else.
}
}
I will answer the question in the update, about events in IE:
function track_file(evt)
{
if (evt == undefined)
{
evt = window.event; // For IE
}
// Use evt
}
is the classical way to get consistent event object across browsers.
After that, I would use regexes to normalize the URL, but I am not sure what you look after.
[EDIT] Some real code to put in practice what I wrote above... :-)
function CheckTarget(evt)
{
if (evt == undefined)
{
// For IE
evt = window.event;
//~ event.returnValue = false;
var target = evt.srcElement;
var console = { log: alert };
}
else
{
target = evt.target;
//~ preventDefault();
}
alert(target.hostname + " vs. " + window.location.hostname);
var re = /^https?:\/\/[\w.-]*?([\w-]+\.[a-z]+)\/.*$/;
var strippedURL = window.location.href.match(re);
if (strippedURL == null)
{
// Oops! (?)
alert("Where are we?");
return false;
}
alert(window.location.href + " => " + strippedURL);
var strippedTarget = target.href.match(re);
if (strippedTarget == null)
{
// Oops! (?)
alert("What is it?");
return false;
}
alert(target + " => " + strippedTarget);
if (strippedURL[1] == strippedTarget[1])
{
//~ window.location.href = target.href; // Go there
return true; // Accept the jump
}
return false;
}
That's test code, not production code, obviously!
The lines with //~ comments show the alternative way of preventing the click on link to do the jump. It is, somehow, more efficient because if I use Firebug's console.log, curiously the return false is ineffective.
I used here the behavior "follow link or not", not knowing the real final purpose.
As pointed out in comments, the RE can be simpler by using hostname instead of href... I leave as it because it was already coded and might be useful in other cases.
Some special precautions should be taken in both cases to handle special cases, like localhost, IP addresses, ports...
I got rid of the domain name, before re-reading the question and seeing it wasn't a problem... Well, perhaps it can be useful to somebody else.
Note: I shown a similar solution in a question to decorate links: Editing all external links with javascript
Given a click event and the original target element, this should work for the original question:
if(target.protocol == window.location.protocol && target.host == window.location.host){
}
Browsers nicely convert the link from the various patterns mentioned by #Tom into full links, so the protocol and host values simply need to match your domain.
if( someDomElementWhichIsALink.href.indexOf(window.location) != -1 ) {
// this is targeting your domain
}

Categories