How can I execute script after wordpress page contents fully loaded - javascript

I'm making a plugin for wordpress post page and I'm trying to detect div with id "matched". I can see the div with browser tools, however console throws the message that the div wasn't found. I think that's because script is loading before the page contents. How can I make it to load after the post contents has rendered?
<script type="text/javascript">
var question = document.getElementById("matched");
window.onload = function afterWebPageLoad() {
if(question){
console.log("id 'matched' exists");
} else {
console.log("id 'matched' not found");
}
}
</script>

You were calling document.getElementById before waiting for the page to load with the onload listener.
Move the variable declaration inside the onload listener so that it is only called when the page is loaded.
What your code should look like:
<script type="text/javascript">
window.onload = function afterWebPageLoad() {
var question = document.getElementById("matched"); // <-- Moved inside
if (question) {
console.log("id 'matched' exists");
} else {
console.log("id 'matched' not found");
}
}
</script>
<p id="matched">match element</p>

You can use setInterval like this;
let checker = setInterval(function () {
if (document.getElementById("matched") !== null) {
console.log("found")
clearInterval(checker)
} else {
console.log("checking..")
}
}, 100)
Also, you must check the element in afterWebPageLoad function. You must move question variable to the function.

Related

Pause loading script and resume after load element of DOM

I need the js script to stop and wait until a certain element appears on the page.
Can this be done with promises?
The fact is that the portal on which the script is used dynamically loads pages without going to the URL, and the necessary part of the script is to be executed on a specific page
Instead of stoping execution, you can move the code into into its own function and execute only after the element appears in DOM.
var check = setInterval("checkElem", 200);
function checkClear() {
clearInterval(check);
}
function checkElem() {
const el1 = document.getElementById("element1");
if (el1 !== null) {
console.log("executing");
yourFunction();
checkClear();
} else {
console.log("element does NOT exist");
}
}
function yourFunction() {
//your executable code
return "some-value";
}

Cordova InAppBowser not loading DOM of result of each page after executescript

All,
Am using cordova inappbrowser. I load the a page uisng window.open. Then hook to loadstop and execute a javascript . The result of execution is another web page. Now I want to manipulate the document elements in that page. So i hook to another laodstop event, but that is not getting my second page. Some docs show , executescript returns an array, but not sure where the html dom is contained.
<script>
var inAppBrowserRef;
function OpenSRMS() {
inAppBrowserRef = window.open('myink','_blank','location=no');
inAppBrowserRef.addEventListener('loadstop', loginSRMS);
}
function loginSRMS() {
if (inAppBrowserRef != undefined) {
inAppBrowserRef.executeScript({ code: "document.getElementById('userName').value = 'Training';" +
"document.getElementById('password').value = 'Training';" +
"document.getElementById(\"frm\").submit();"});
}
inAppBrowserRef.addEventListener('loadstop', INQCS);
}
function INQCS(){
if (inAppBrowserRef != undefined) {
inAppBrowserRef.executeScript({ code: "alert('hello');"});
}
}
</script>

Javascript setTimeout fires as many times in as page loaded by ajax

I am executing setTimeout function in a page which loads via ajax call. but if i click the link to load the page again, i afraid the last setTimeout call still continues and the number of intervals of the calls set by setTimeout executes multiple times.
tried this is the remote page:
var RefreshInterval = null;
clearTimeout(RefreshInterval);
function someFunction()
{
....
setNextRefresh();
}
function setNextRefresh() {
console.log(wifiRadarRefreshInterval);
RefreshInterval = null;
clearTimeout(RefreshInterval);
RefreshInterval = setTimeout('someFunction();', 20*1000);
}
declare var RefreshInterval = null; outside of page loaded by ajax and use this code on the page:
clearTimeout(RefreshInterval);
function someFunction()
{
....
setNextRefresh();
}
function setNextRefresh() {
console.log(wifiRadarRefreshInterval);
clearTimeout(RefreshInterval);
RefreshInterval = setTimeout('someFunction();', 20*1000);
}
if i don't want to declare it in parent page, here is the solution i found:
//Clear previously loaded calls if exists
try{ clearTimeout(wifiRadarRefreshInterval); }catch(e){}
var wifiRadarRefreshInterval = null;
function somefunction(){
....
setNextRefresh();
}
function setNextRefresh() {
try{
clearTimeout(wifiRadarRefreshInterval);
wifiRadarRefreshInterval = null;
wifiRadarRefreshInterval = setTimeout('somefunction();', 20*1000);
}
catch(e){
console.log(e.message + e.stack);
}
}
Do not use this
var RefreshInterval = null;
clearTimeout(RefreshInterval);
You are actually assigning a null and then trying to clear it. Which will not work, The timeout must be cleared by using the clearTimeout and by passing the variable which was assigned to the setTimeout. Here you will end up passing a null so the timer is never cleared.
Here is a small sample which will demonstrate a fix to your problem JS Fiddle
So insted of setting the variable to null and then trying to clear it, Just check if the variable is not defined and if it is defined clear it, else move on. Use the code below, Also you must remove the top two lines as mentioned
function setNextRefresh() {
console.log(wifiRadarRefreshInterval);
if (typeof RefreshInterval !== 'undefined') {
clearTimeout(RefreshInterval);
}
RefreshInterval = setTimeout('someFunction();', 20*1000);
}
Click on the button say like 4 times, The output should be printed only once. That is if the ajax call is made 4 times the set time out must execute only once. Check below snippet for demo
var clickCount= 0; // just to maintain the ajax calls count
function NewPageSimilator(clicksTillNow) { // this acts as a new page. Let load this entire thing on ajaX call
if (typeof RefreshInterval !== 'undefined') {
clearTimeout(RefreshInterval);
}
function setNextRefresh() {
window.RefreshInterval = setTimeout(printTime, 3000); //20*1000
}
function printTime() {// dumy function to print time
document.getElementById("output").innerHTML += "I was created at click number " + clicksTillNow + '<br/>';
}
setNextRefresh();
}
document.getElementById("ajaxCall").addEventListener("click", function() { // this will act as a ajax call by loading the scripts again
clickCount++;
NewPageSimilator(clickCount);
});
document.getElementById("clear").addEventListener("click", function() { //reset demo
clickCount = 0;
document.getElementById("output").innerHTML = "";
});
<p id="output">
</p>
<button id="ajaxCall">
AJAX CALL
</button>
<button id="clear">
Clear
</button>

javascript html5 history, variable initialization and popState

main question
Is there a javascript way to identify if we are accessing a page for the first time or it is a cause of a back?
My problem
I'm implementing html5 navigation in my ajax driven webpage.
On the main script, I initialize a variable with some values.
<script>
var awnsers=[];
process(awnsers);
<script>
Process(awnsers) will update the view according to the given awnsers, using ajax.
In the funciton that calls ajax, and replaces the view, I store the history
history.pushState(state, "", "");
I defined the popstate also, where I restore the view according to the back. Moreover, I modify the global variable awnsers for the old value.
function popState(event) {
if (event.state) {
state = event.state;
awnsers=state.awnsers;
updateView(state.view);
}
}
Navigation (back and forth) goes corectly except when I go to an external page, and press back (arrving to my page again).
As we are accessing the page, first, the main script is called,the valiable awnsers is updated, and the ajax starts. Meanwile, the pop state event is called, and updates the view. After that the main ajax ends, and updates the view according to empty values.
So I need the code:
<script>
var awnsers=[];
process(awnsers);
<script>
only be called when the user enters the page but NOT when it is a back. Any way to do this?
THanks!
Possible solution
After the first awnser I have thought of a possible solution. Tested and works, whoever, I don't know if there is any cleaner solution. I add the changes that I've done.
First I add:
$(function() {
justLoaded=true;
});
then I modify the popState function, so that is in charge to initialize the variables
function popState(event) {
if (event.state) {
state = event.state;
awnsers=state.awnsers;
updateView(state.view);
} else if(justLoaded){
awnsers=[];
process(awnsers);
}
justLoaded=false;
}
Thats all.
what about using a global variable?
var hasLoaded = false;
// this function can be called by dom ready or window load
function onPageLoad() {
hasLoaded = true;
}
// this function is called when you user presses browser back button and they are still on your page
function onBack() {
if (hasLoaded) {
// came by back button and page was loaded
}
else {
// page wasn't loaded. this is first visit of the page
}
}
Use cookie to store the current state.
yeah! This is what I have:
var popped = (($.browser.msie && parseInt($.browser.version, 10) < 9) ? 'state' in window.history : window.history.hasOwnProperty('state')), initialURL = location.href;
$(window).on('popstate', function (event) {
var initialPop = !popped && location.href === initialURL, state;
popped = true;
if (initialPop) { return; }
state = event.originalEvent.state;
if (state && state.reset) {
if (history.state === state) {
$.ajax({url: state.loc,
success: function (response) {
$(".fragment").fadeOut(100, function () {
$(".fragment").html($(".fragment", response).html()).fadeIn(100);
);
document.title = response.match(/<title>(.*)<\/title>/)[1];
}
});
} else { history.go(0); }
else {window.location = window.location.href; }
});
And:
$.ajax({url:link,
success: function (response) {
var replace = args.replace.split(",");
$.each(replace, function (i) {
replace[i] += ($(replace[i]).find("#video-content").length > 0) ? " #video-content" : "";
var selector = ".fragment "+replace[i];
$(selector).fadeOut(100, function () {
$(selector).html($(selector,response).html()).fadeIn(100, function () {
if (base.children("span[data-video]")[0]) {
if ($.browser.msie && parseInt($.browser.version, 10) === 7) {
$("#theVideo").html("");
_.videoPlayer();
} else {
_.player.cueVideoById(base.children("span[data-video]").attr("data-video"));
}
}
});
});
});
document.title = response.match(/<title>(.*)<\/title>/)[1];
window.history.ready = true;
if (history && history.pushState) { history.pushState({reset:true, loc:link}, null, link); }
}
});

jQuery .ready in a dynamically inserted iframe

We are using jQuery thickbox to dynamically display an iframe when someone clicks on a picture. In this iframe, we are using galleria a javascript library to display multiple pictures.
The problem seems to be that $(document).ready in the iframe seems to be fired too soon and the iframe content isn't even loaded yet, so galleria code is not applied properly on the DOM elements. $(document).ready seems to use the iframe parent ready state to decide if the iframe is ready.
If we extract the function called by document ready in a separate function and call it after a timeout of 100 ms. It works, but we can't take the chance in production with a slow computer.
$(document).ready(function() { setTimeout(ApplyGalleria, 100); });
My question: which jQuery event should we bind to to be able to execute our code when the dynamic iframe is ready and not just it's a parent?
I answered a similar question (see Javascript callback when IFRAME is finished loading?).
You can obtain control over the iframe load event with the following code:
function callIframe(url, callback) {
$(document.body).append('<IFRAME id="myId" ...>');
$('iframe#myId').attr('src', url);
$('iframe#myId').load(function() {
callback(this);
});
}
In dealing with iframes I found good enough to use load event instead of document ready event.
Using jQuery 1.3.2 the following worked for me:
$('iframe').ready(function() {
$('body', $('iframe').contents()).html('Hello World!');
});
REVISION:!
Actually the above code sometimes looks like it works in Firefox, never looks like it works in Opera.
Instead I implemented a polling solution for my purposes. Simplified down it looks like this:
$(function() {
function manipIframe() {
el = $('body', $('iframe').contents());
if (el.length != 1) {
setTimeout(manipIframe, 100);
return;
}
el.html('Hello World!');
}
manipIframe();
});
This doesn't require code in the called iframe pages. All code resides and executes from the parent frame/window.
In IFrames I usually solve this problem by putting a small script to the very end of the block:
<body>
The content of your IFrame
<script type="text/javascript">
//<![CDATA[
fireOnReadyEvent();
parent.IFrameLoaded();
//]]>
</script>
</body>
This work most of the time for me. Sometimes the simplest and most naive solution is the most appropriate.
Following DrJokepu's and David Murdoch idea I implemented a more complete version.
It requires jQuery on both the parent and iframe and the iframe to be in your control.
iframe code:
var iframe = window.frameElement;
if (iframe){
iframe.contentDocument = document;//normalization: some browsers don't set the contentDocument, only the contentWindow
var parent = window.parent;
$(parent.document).ready(function(){//wait for parent to make sure it has jQuery ready
var parent$ = parent.jQuery;
parent$(iframe).trigger("iframeloading");
$(function(){
parent$(iframe).trigger("iframeready");
});
$(window).load(function(){//kind of unnecessary, but here for completion
parent$(iframe).trigger("iframeloaded");
});
$(window).unload(function(e){//not possible to prevent default
parent$(iframe).trigger("iframeunloaded");
});
$(window).on("beforeunload",function(){
parent$(iframe).trigger("iframebeforeunload");
});
});
}
parent test code:
$(function(){
$("iframe").on("iframeloading iframeready iframeloaded iframebeforeunload iframeunloaded", function(e){
console.log(e.type);
});
});
Found the solution to the problem.
When you click on a thickbox link that open a iframe, it insert an iframe with an id of TB_iframeContent.
Instead of relying on the $(document).ready event in the iframe code, I just have to bind to the load event of the iframe in the parent document:
$('#TB_iframeContent', top.document).load(ApplyGalleria);
This code is in the iframe but binds to an event of a control in the parent document. It works in FireFox and IE.
This function from this answer is the best way to handle this as $.ready explicitly fails for iframes. Here's the decision not to support this.
The load event also doesn't fire if the iframe has already loaded. Very frustrating that this remains a problem in 2020!
function onIframeReady($i, successFn, errorFn) {
try {
const iCon = $i.first()[0].contentWindow,
bl = "about:blank",
compl = "complete";
const callCallback = () => {
try {
const $con = $i.contents();
if($con.length === 0) { // https://git.io/vV8yU
throw new Error("iframe inaccessible");
}
successFn($con);
} catch(e) { // accessing contents failed
errorFn();
}
};
const observeOnload = () => {
$i.on("load.jqueryMark", () => {
try {
const src = $i.attr("src").trim(),
href = iCon.location.href;
if(href !== bl || src === bl || src === "") {
$i.off("load.jqueryMark");
callCallback();
}
} catch(e) {
errorFn();
}
});
};
if(iCon.document.readyState === compl) {
const src = $i.attr("src").trim(),
href = iCon.location.href;
if(href === bl && src !== bl && src !== "") {
observeOnload();
} else {
callCallback();
}
} else {
observeOnload();
}
} catch(e) {
errorFn();
}
}
Basically what others have already posted but IMHO a bit cleaner:
$('<iframe/>', {
src: 'https://example.com/',
load: function() {
alert("loaded")
}
}).appendTo('body');
Try this,
<iframe id="testframe" src="about:blank" onload="if (testframe.location.href != 'about:blank') testframe_loaded()"></iframe>
All you need to do then is create the JavaScript function testframe_loaded().
I'm loading the PDF with jQuery ajax into browser cache. Then I create embedded element with data already in browser cache. I guess it will work with iframe too.
var url = "http://example.com/my.pdf";
// show spinner
$.mobile.showPageLoadingMsg('b', note, false);
$.ajax({
url: url,
cache: true,
mimeType: 'application/pdf',
success: function () {
// display cached data
$(scroller).append('<embed type="application/pdf" src="' + url + '" />');
// hide spinner
$.mobile.hidePageLoadingMsg();
}
});
You have to set your http headers correctly as well.
HttpContext.Response.Expires = 1;
HttpContext.Response.Cache.SetNoServerCaching();
HttpContext.Response.Cache.SetAllowResponseInBrowserHistory(false);
HttpContext.Response.CacheControl = "Private";
This was the exact issue I ran into with our client. I created a little jquery plugin that seems to work for iframe readiness. It uses polling to check the iframe document readyState combined with the inner document url combined with the iframe source to make sure the iframe is in fact "ready".
The issue with "onload" is that you need access to the actual iframe being added to the DOM, if you don't then you need to try to catch the iframe loading which if it is cached then you may not. What I needed was a script that could be called anytime, and determine whether or not the iframe was "ready" or not.
Here's the question:
Holy grail for determining whether or not local iframe has loaded
and here's the jsfiddle I eventually came up with.
https://jsfiddle.net/q0smjkh5/10/
In the jsfiddle above, I am waiting for onload to append an iframe to the dom, then checking iframe's inner document's ready state - which should be cross domain because it's pointed to wikipedia - but Chrome seems to report "complete". The plug-in's iready method then gets called when the iframe is in fact ready. The callback tries to check the inner document's ready state again - this time reporting a cross domain request (which is correct) - anyway it seems to work for what I need and hope it helps others.
<script>
(function($, document, undefined) {
$.fn["iready"] = function(callback) {
var ifr = this.filter("iframe"),
arg = arguments,
src = this,
clc = null, // collection
lng = 50, // length of time to wait between intervals
ivl = -1, // interval id
chk = function(ifr) {
try {
var cnt = ifr.contents(),
doc = cnt[0],
src = ifr.attr("src"),
url = doc.URL;
switch (doc.readyState) {
case "complete":
if (!src || src === "about:blank") {
// we don't care about empty iframes
ifr.data("ready", "true");
} else if (!url || url === "about:blank") {
// empty document still needs loaded
ifr.data("ready", undefined);
} else {
// not an empty iframe and not an empty src
// should be loaded
ifr.data("ready", true);
}
break;
case "interactive":
ifr.data("ready", "true");
break;
case "loading":
default:
// still loading
break;
}
} catch (ignore) {
// as far as we're concerned the iframe is ready
// since we won't be able to access it cross domain
ifr.data("ready", "true");
}
return ifr.data("ready") === "true";
};
if (ifr.length) {
ifr.each(function() {
if (!$(this).data("ready")) {
// add to collection
clc = (clc) ? clc.add($(this)) : $(this);
}
});
if (clc) {
ivl = setInterval(function() {
var rd = true;
clc.each(function() {
if (!$(this).data("ready")) {
if (!chk($(this))) {
rd = false;
}
}
});
if (rd) {
clearInterval(ivl);
clc = null;
callback.apply(src, arg);
}
}, lng);
} else {
clc = null;
callback.apply(src, arg);
}
} else {
clc = null;
callback.apply(this, arguments);
}
return this;
};
}(jQuery, document));
</script>

Categories

Resources