I'd like to have code executed when a particular element and all of it's children are in the DOM. I know how to poll for the desired element's existence, or even better, to use a MutationObserver, but the desired element is itself rather large and I can't be sure that all of it's children are fully loaded simply based on it existing.
I could wait for ready which is called when all the DOM is loaded, but the page usually takes a rather long time to load. In the interests of speed i'd like to know without necessarily waiting for $(document).ready().
I did find the on function, I love the fact that it will be called for elements which don't even exist yet:
$(document).on('SomeEvent', '#desiredElem', handler);
...however I don't know of an event which is fired for an html element being fully in the DOM.
My script is being injected into the browser, and I know from logging that it's running a long time before $(document).ready() or DOMContentLoaded. Basically i'd like to take advantage of that. I can't add <script> tags to the HTML, unfortunately.
On a side note, an event for an object existing would be interesting. It would save me from having to use MutationObserver.
Since you've said that what you're trying to discern is when an element and all of its children that are present in the source HTML of the page are loaded, there are a couple things you can do:
You can test for the presence of any known element after the last child. Since HTML elements are loaded serially in order, if the element after the last child is present, then everything before it must already be in the DOM because page HTML is inserted only in the order it appears in the page HTML source.
If you put a <script> tag after the relevant HTML in the page, then all HTML before that <script> tag will already be in the DOM when that script tag runs.
You can either poll or use a mutation observer, but chances are this won't really help you because no scripts run while the DOM parser is in the middle of inserting a bunch of HTML into the page. So, your scripts or mutation events would only run after the whole page DOM was inserted anyway or when the page pauses in order to load some other inline resource such as a <script> tag.
You can fallback to the DOMContentLoaded event which will tell you when the whole DOM is loaded.
For more concrete help, we need to understand much more about your specific situation including an example of the HTML you're trying to watch for and a full understanding of exactly what constraints you have (what you can modify in the page source and where in the page you can insert or run scripts).
You need to setup a timer and keep observing the DOM checking if the element exists or not
function checkIfLoaded( callBack, elementSelector )
setTimeout( function(){
$( elementSelector ).size() == 0 )
{
//continue checking
checkIfLoaded( callBack, elementSelector )
}
else
{
callBack();
}
}, 1000 );
)
checkIfLoaded( function(){ alert( "div loaded now" ) }, "#divId" );
Related
I am having an issue with view components in .NET Core 2.0. I need to be able to detect when a view component has finished loading in the parent view.
Once the view component has loaded, I need to set focus on a specific field that is part of the view component.
Currently, I am using JQuery window.onload(). However, in window.onload() the view component and any subsequent JavaScript has not fully render yet.
Since it has not fully rendered the window.onload event can't find the specific field in the view component.
If I use setTimeout and set it's ms between 1000 and 3000, thus giving the view component time to finish loading, it works fine.
The problem with using setTimeout is that it isn't consistent. Depending on how long the page takes to load it may or may not set the focus on the specified field.
Here is the code jquery code.
var setSearchFocus = function () {
if ($("#divSearch").is(":visible")) {
$('#Diagnosis_Search').focus();
}
}
window.onload = function () {
setTimeout(setSearchFocus, 1000);
}
divSearch is the div in the parent view where the view component is rendered.
Diagnosis_Search is the name of the field in the view component that needs to receive focus.
Appreciate any help with an alternate way to determine when a page has completely loaded or the ablity to detect when a view component has finished loading.
Thanks!
If you're up to using jQuery, you're better off using the document ready event:
$(document).ready(function() {
setTimeout(setSearchFocus, 1000);
});
OnLoad will fire before the entire document/page is ready. Using the $(document).ready(... approach will wait until your page is ready (in other words, when the DOM elements you want to interact with are present and rendered).
This is one of the most common problems on web development. You're using window.onload which is not jquery. It is part of the Document Object Model (DOM) and as you have noticed it doesn't work as expected. This is why the guys # jquery came up with document ready:
$( document ).ready(function() {
console.log( "ready!" );
});
or just
$(function() { console.log("ready!"); });
https://learn.jquery.com/using-jquery-core/document-ready/
UPDATE: as per the comment on the other answer, I get that you've got to wait until an iframe loads your view component. Is that so? If it is, then try to listen to the iframe's document object, an iframe is like another whole webpage embedded on your site, so there's another document object for it. You can access this object from the parent by using
document.getElementById('divSearch').contentWindow.document
you see a document that contains other document, which is actually what we're doing with the iframe. Beware this line isn't going to work if the document you're loading on the iframe is not on the same origin (not part or the same web or in the same TLD), but as you told us that this is a viewComponent this isn't the case probably.
To sum up, try with $(iframe#youriframeid).ready(function() { console.log("ready!"); }) instead.
I have code I'd like to run before the page is rendered. For example updating dates from absolute time to relative time or converting raw text (or markdown) to html. If I reload the page several times I can see occasionally there's flickering updating the changes. How do I run the code as it's drawn instead at the end where it needs to redraw everything?
I tried document.addEventListener('beforeload' it appears that event is depreciated and no longer supported in chrome
You'll need to think about your page lifecycle.
When your page is being loaded, if a <script> tag is encountered, it will immediately be executed, and prevent any more content being rendered until that script is complete. This isn't recommended practice.
But, to answer your question, you could do something like this:
<p>Your next reward will be available in <span id="nextRewardTime">3600</span>.</p>
<script type="text/javascript">
var $el = $('#nextRewardTime');
$el.text(format($el.text());
</script>
<p>Come back soon!</p>
Now, immediately after the first <p> is downloaded by the browser, it'll hit the <script> tag and execute the JS. Only once that is complete will it download the next <p> tag ('Come back soon!').
You can't attach an event handler to the Javascript before it's been rendered on the DOM, because... well... it's not on the DOM.
The better way of doing this is to format the thing correctly on the server in the first place.
Edit: Also, scattering script tags throughout your page makes your code really unmaintainable!
I would use document.readyState and a self-invoking function:
<body>
<script>
function executeFunction() {
//do stuff - such as check for anchor tags
$("a").html('Replaced');
if (document.readyState === 'loading') executeFunction(); // repeat to ensure all the tags get innerHTML-ed
}
(function(){
if (document.readyState === 'loading') executeFunction();
})();
</script>
<!-- more HTML -->
</body>
It seems that you want JS to execute as the page is loading. You may also want to try using uninitialized as the ready state (which occurs before "loading").
I'm using PhantomJS as a crawler; if there is no JS in a page I can assume that it's completely loaded when onLoadFinished fires, but if there is JS in a page, I need to wait a bit to give the scripts a chance to do stuff. This is my current stab at detecting JS:
var pageHasJS = page.evaluate(function () {
return (document.getElementsByTagName("script").length > 0 ||
document.evaluate("count(//#*[starts-with(name(), 'on')])",
document, null, XPathResult.NUMBER_TYPE,
null).numberValue > 0);
})
This looks for <script> tags and for elements with an onsomething attribute.
Q1: Is there any other HTML construct that can sneak JS into a page? javascript: URLs do not count, because nothing will ever get clicked.
Q2: Is there a better way to do the second test? I believe it is not possible to do that with querySelector, hence resorting to XPath, but maybe there is some other feature that would accomplish the same task.
Q3: The crawler does not interact with the page once it is loaded. The onload event is the only legacy event attribute that I know of that fires in the absence of user interaction. Are there any others? In other words, would it be safe to replace the second test with document.evaluate("count(//#onload)", ...) or maybe even !!document.body.getAttribute("onload")?
Instead of checking for script tags and giving fixed amount of time, you can intercept the actual HTTP request (take a look at onResourceRequested / onResourceReceived) and take the screenshot after all resources have been loaded. Take a look at ajax-render
I'm trying to write a very simple user content script for google.com, but the problem is that google's source code is lengthy. I want to execute code in javascript the instant that an element is in the document, but before the whole document has loaded.
Essentially, I wan't to change the source of an image before the rest of the page loads. I also want to modify html in a certain other div with a specific id. But again, I don't want to wait for the rest of the document to load before I start doing it.
How can I accomplish this? I am using jquery.
Try this:
var element = $([TagName]) || document.getElementsByTagName([TagName])[[occurance]];
function doSomething() {
// Do some magic.
}
element.addEventListener("load", doSomething, false); // Notice no brackets after the function call.
This adds an event listener to the element, and will wait until it has fully loaded until it runs the function (doSomething()).
Hope this helps.
I have a history API script that loads new page content without the need for a page refresh. I have come into a problem with inline scripts, where the scripts are evaluated by jQuery even if they have been done so previously. So for example, if someone re-visits a page with an inline script that script will be executed each time they re-visit. This causes problems as say if a DOM element is added in a script than that element will be added several times if they have visited that page several times.
For reasons I won't go into I cannot put these inline scripts into an external file and load them that way.
Here's the coded that deals with the scripts;
dom.filter('script').each(function(){//function to allow inline javascript, has to be after page fadeIn incase scripts reference page DOM
$.globalEval(this.text || this.textContent || this.innerHTML || '');
var script_src = ($(this).attr('src'));
if (script_src === 'AJAX/request_feed.js' || script_src === 'js/profile.js'){
$(window).unbind('scroll');
$.getScript(script_src);
}
});
Should you require any more parts from the whole history script just ask. i don't think they're required though.
Note: The if clause is there for a scroll loader i have. I have two scroll_loaders so the scroll event needs to be unbinded and binded each time. No need to worry about that though.
You could store your executed scripts in a cookie, then check the cookie before executing. One way or another, you'll need some way of keeping track of what has been executed and what hasn't been.
Alternate Suggestion
You could tweak your scripts to be self regulating:
myscript.js:
(function(){
if ($(this).parent().script_registry.inArray('myscript')) return false;
$(this).parent().script_registry.push('myscript'); // Register this script as launched
alert('Do stuff..');
});
Note: The above code may not be 100% syntactically correct.