I'm using the Boxer library from www.formstone.it to display a modal popup window over my HTML page. On triggering the modal window, HTML content gets loaded into the modal DIV from a file on my server. The Boxer code:
$(".boxer.boxer_object").click(function(e) {
e.preventDefault();
e.stopPropagation();
$obj = $('<div class="outer_container" />').load("http://www.myserver.com/game_modal.html",function(){
setTimeout(function (){
... Kinetic code which loads several image and GUI elements for a simple game ...
}, 2000); // delay Kinetic code execution with 2 seconds
$.boxer($obj);
});
Even though it does seemingly only execute the KineticJS code AFTER the HTML code has loaded, I do still sporadically get the following error:
Uncaught TypeError: Cannot set property 'innerHTML' of null
As I understand it, this error occurs when the canvas is trying to target a DIV which does not yet exist. In other words, the KineticJS code executes AFTER the code has loaded but BEFORE the relevant container DIV has become part of the page structure.
As seen in my code above, I now use a setTimeout() function to delay the KineticJS code execution with 2 seconds. Even though less than ideal, I have not seen the error again, with the game graphics loading every time. However, this is a fix that may be working on my browser but which may fail for someone else in other conditions.
Is there a proper way in which to ensure that the KineticJS code will ALWAYS execute AFTER the externally loaded HTML code has become part of the page structure? i.e. after the container DIV which the KineticJS code targets for the HTML5 canvas actually exists?
You should be able to skip the ajax call and render the game's container and loading markup manually:
var $game = $('<div id="outside_container" style="text-align:center; width:900px; height=600px;"><span style="display:inline-block; line-height:600px; font-size: 4em;">LOADING...</span></div>');
Then use the 'callback' option to initialize the game:
$boxer($game, {
callback: initGame
});
function initGame() {
// kinetic js code
...
}
Disclaimer: I haven't used boxer.
I took a quick peek at your boxer link.
There is a callback which executes "after opening instance".
How about putting your Kinetic code in that callback function you can supply to boxer.
From what I can understand, you are trying to create a div with the class of 'outer_container' when the onclick event occurs. You then want to 'load' your game modal from your web service and then run the kinetic js and boxer code asynchronously (via the callback) when it has been returned.
Asynchronous functionality can always been a bit fiddly. In your asynchronous chain of events, I think creating the div at the same time as attaching a .load() function to it means that sometimes the web service may be ready before the div has been created.
Have you tried creating the divelement before calling a web service pointing at that element?
You could either create thediv when you first intialise the page or try this...
$(".boxer.boxer_object").click(function(e) {
e.preventDefault();
e.stopPropagation();
//create the div first before the web service call to ensure it exists...
var obj = document.createElement('div');
obj.setAttribute('class', 'outer_container');
$('.outer_container').load("http://www.myserver.com/game_modal.html",function(){
//your code here, called after the web service has returned data...
$.boxer(obj);
});
I would personally just declare the 'outer_container' div before the declaration of the onclick event.
I hope this helps :).
Related
I'm trying to use the pushPage() function of the OnsenUI 2 (rc15 as of now). Alongside the OnsenUI, I'm using jQuery 3.
Here is my function that upon clicking certain elements should push a page:
$(".tile_handler").on("click", ".imalink", function () {
var link = $(this).data().href;
if(link != null){
document.querySelector("#myNavigator").pushPage(link, { animation: "slide-ios" });
}
})
When I push the page for the first time, it works fine. I use the iOS back button to return. Then I click it again and I get this error (and increasingly more as I repeat the process):
[index.js:450] Uncaught (in promise) pushPage is already running.
Here is another function that is supposed to load splitter pages:
$(".splitter_item").click(function () {
var address = $(this).data('address');
$('#content')[0].load(address).then(menu.close.bind($('#menu')[0]));
})
When I switch between two pages via Splitter it starts throwing this ( and more every time I switch between pages)
[undefined:1] Uncaught (in promise) Splitter side is locked.
What I assume is happening is that I load a page, leave it and when I access it again, it loads the page again. However that doesn't seem to be the behavior shown in OnsenUI examples such as this:
document.addEventListener('init', function(event) {
var page = event.target;
if (page.id === 'page1') {
page.querySelector('#push-button').onclick = function() {
document.querySelector('#myNavigator').pushPage('page2.html', {data: {title: 'Page 2'}});
};
} else if (page.id === 'page2') {
page.querySelector('ons-toolbar .center').innerHTML = page.data.title;
}
});
There is the popPage() function which should delete previously loaded page and would prevent this. But the references don't use it, so I assume I'm doing something wrong. But I don't know what.
UPDATE:
I managed to reproduce both problems in CodePen. Here is the Splitter error and here is the pushPage() one. It seems like the pushPage() one is a problem with my function as it adds a pushPage() request every time I click it, not sure why though.
Both errors only seem to happen in Ripple emulator (via VS2015). I don't seem to be able to reproduce them in Android Emulator (but the $(".tile_handler").on("click", ".imalink", function () { code fires incorreclty multiple times anyway). I'm testing this further.
Basically each time you push a page, that page fires an init event. However Onsen still keeps the initial page in the dom.
Navigator example (the same logic applies to the splitter one):
<ons-navigator>
<ons-page id="dashboard">
<div class="imalink" data-href="request_list.html"></div>
</ons-page>
</ons-navigator>
You have an init event for dashboard. Then you click the tile and go to another page.
Then request_list fires its own init event. However our initial page is still in the dom.
<ons-navigator>
<ons-page id="dashboard" style="display: none">
<div class="imalink" data-href="request_list.html"></div>
</ons-page>
<ons-page id="request_list">
...
</ons-page>
</ons-navigator>
You have something like this. However the following is called a second time:
$(".tile_handler").on("click", ".imalink", function () {
...
})
Which adds the listeners once more. $el.on("click") like an alias of addEventListener, meaning you are adding more and more listeners.
So whenever you navigate you keep adding them because the initial page was never removed from the dom.
Alternative solutions:
Use only the current page (e.target)
$('selector') // instead of this
$(e.target).find('selector') // use this
That way you limit to only finding elements within the page which you just created.
Enable the handlers from the start.
Since you are using jQuery there is actually a simpler way to do the things without relying on the init event.
Just do this outside of any init handler:
$(document).on("click", ".tile_handler .imalink", function () { ... })
This actually means that the handler is attached to the document itself and the handler will be called only when the target is .tile_handler .imalink - so it works with any future imalinks which you create.
This may not be the most efficient approach, but is definitely one of the simplest.
Both errors which you mention are actually a safe measure from you pushing or doing some action twice by mistake if you double click a button/link for example.
They appear when you try to make an action while an animation is running. As you can see in the examples generally there is no problem to push a page twice or more, as long as you start the second push after the first one is finished.
Here are demos with your exact code for both the splitter and navigator.
So the error does not come from the code which you provided, its someplace elsewhere.
The only thing I can come up with is that the behaviour which you mentioned would be seen if for some reason the popPage method failed to finish correctly. Maybe if you provide us with your own codepen where the issue can be reproduced we could debug it further.
An alternative, though highly NOT recommended would be to force the state before you do your action. This however would not be solving the problem, rather only masking it. And of course as with all hacks - it may break in future versions.
myNavigator._isRunning = false;
UPDATE:
Here are the two updated pens which you gave in the comments:
https://codepen.io/IliaSky/pen/YWOOkW?editors=1010
https://codepen.io/IliaSky/pen/QEVVGm?editors=1010
Basically you were adding the handlers on init event, which is fired whenever a page is added. so with your logic you are adding more and more handlers with each page. Just make sure you add them only once and you will be fine.
Add things like:
if (e.target.id == 'pagename') ...
or simply
$(document).on("init", '#dashboard_page', function(){ ... }
Take a look at .one() from jQuery - the event handler will only execute once per element in order to prevent the error: Uncaught (in promise) pushPage is already running
$(".tile_handler").one("click", ".imalink", function () {
var link = $(this).data().href;
if(link != null){
document.querySelector("#myNavigator").pushPage(link, { animation: "slide-ios" });
}})
I have a Javascript in my header that calls an external server to retrieve some information, like this:
$.getJSON("https://domain.tld/json/", function (something) {
var results = //stuff that formats the result, not relevant here;
window.$info = results;
}).fail(function(){
// here's where I need the code to make the warning appear
});
Then in the body I have a warning that should pop up in case the GET request failed:
<div id="warning">Warning text</div>
Problem: the javascript is in the header and I can't change the "warning" div because it has not been created yet.
If I use document.getElementById("warning").style.display='block'; I get an error message saying it is null.
So how do I use the .fail() part of the jquery GET function to make the warning div appear (that is created later) in case the GET function has failed? Or is that not possible? And some 'solution' I tried (don't remember what exactly) even delayed the loading of the whole page. I'd like to avoid that as well.
Thank you for helping me out.
You should hide your html element initialy
<div id="warning" style="display:none;">Warning text</div>
And you can call show() method to display hidden element when get request failed
$("#warning").show();
Your javascript is trying to execute and look for things before the DOM has loaded. You can solve this problem by making sure your javascript only fires when the DOM is ready. In jQuery you do that like this:
$(document).ready(function(){
//your code here
});
The native javascript version of this is much more complicated so I won't post that here. However you could also wait for the entire page to load before executing the code like so:
window.onload = function(){
//your code here
}
I want to use boomrang framework in Jquery to get the bandwidth of the user's network which has to be displayed on the screen as "connection : fair/poor/good".
With on ready,on load, etc.., javascript function will be called only after the elements are ready to be accessed. But, I want the boomrang call to be called quite before that. Please tell me which event I have to use so that function call can happen before the elements of the page loads. Thanks in advance.<>
Note: I have tried by putting script tag at the top of the head tag. But still page elements are getting evaluated first (along with their el expressions).
If you want your function to be called before DOM creation then you dont need to call your function in any onload or on(document).ready, what you have to do is just call your function inside the script tag
For example (Script on the top of the page)
<script>
function abc()
{
// function desc
}
abc(); //Will be called as soon as the url is opened
$(document).ready(function()
{
abc(); // will be called when the DOM is ready
});
</script>
Use bw plugin from boomrang freamework
http://yahoo.github.io/boomerang/doc/howtos/howto-3.html
I am looking for a solution to show a progress indicator when an iframe page changes, but prior to the iframe loading completely.
There are 100's of pages on Stack Overflow going over the differences between jQuery ready(), JavaScript window.load, document.load, DOMContentReady, on('pageinit'...) and after reading the differences on all these various techniques I'm now a bit stuck on how to accomplish trapping the event within the iframe.
So far I have only succeeded in capturing when the iframe has changed once the DOM is built. I would like to be able to detect when a page is about to load so I could have some sort of indicator/spinner in my header.
This is what I have so far (capturing the iframe change on the onload):
.....
<iframe id="rssID" src="http://feeds.reuters.com/reuters/oddlyEnoughNews"
onload="blerg()" style="width: 800px; height:600px"/>
......
$(document).on('pageinit','#index', function(){
alert('pageinit'); //gets called on first load
});
$(document).on('pageinit','#rssID', function(){
alert('pageinit rssFeed'); //nothing happens.
});
function blerg() {
var myIframe = document.getElementById("rssID");
myIframe.onload = func;
};
function func() {
alert("changed");
}
http://jsfiddle.net/Gdxvs/
It would appear that pageinit is the correct path but I have no idea on how to trap that within an iframe. I would prefer not using jQuery, but I'm not sure that is possible without huge amounts of code.
One final: Do I need to use a die("pageinit");
Upon loading, my web site runs this code if the users is using an alternate theme:
ReplaceJSCSSFile("css/skin1.css", "css/skin2.css", "css");
AJAX_LoadResponseIntoElement("skinContainer", "skin" + themeSelect + ".txt", function() {
AJAX_LoadResponseIntoElement("contentdiv", "index.txt", initPage);
});
What AJAX_LoadResponseIntoElement does ought to be obvious, but here's a short explanation of ReplaceJSCSSFile: It basically searches for link elements within the web page with their src property equal to "css/skin1.css", at which point it creates a new link element (src="css/skin2.css") and uses .parentNode.replaceChild to replace the old with the new.
The problem with this is that initPage() sets the positions and of certain divs that move around on the screen in relation to the positions of static elements. The position of those static elements is fetched from CSS, but the CSS is not being applied before the code is running, and initPage() is putting things in the wrong place because of it.
I have tried using a callback:
ReplaceJSCSSFile("css/skin1.css", "css/skin2.css", "css", function() {
AJAX_LoadResponseIntoElement("skinContainer", "skin" + themeSelect + ".txt", function() {
AJAX_LoadResponseIntoElement("contentdiv", "index.txt", initPage);
});
});
Unfortunately, this didn't work. Presumably, it tells the browser to replace the CSS file, and as soon as it tells the browser to do that, it moves on to the callback function, using AJAX to fetch the page contents. I'm thinking the page contents come back prior to the CSS file, which is why the new CSS is not being applied by the time initPage() is being executed.
Is it possible to fix this without using alternate stylesheets?
The only thing that I can come up with other than alternate stylesheets is fetching the CSS contents with AJAX (which would actually wait for the server's response before executing the callback function) to replace the "css/skin1.css" link element.
You can fudge it and use setTimeout in your callback so that the body of the callback isn't run until a bit later. This gives the browser some time to recover.