Fire event when browser parses/renders a DOM element - javascript

Assume that we know that an element (or a very specific selector) is going to appear on a page. Is it possible to set up beforehand, via JS or jQuery, an event that goes off when the browser gets to that element and parses it? This is NOT content loaded through AJAX but is present in the primary page's source.
The reason for this need is that I'm working with a hosted system that greatly limits where and when I can inject code to fix problems with the page. I can pretty much only place my code at the start and end of what is a really long page. Right now, the page has to load completely before it can inject any desired changes (yuck!). Plus, I cannot make the pages shorter in content.
This is basically the process I would like to happen:
Page begins loading
Listener set up to watch for .specialClass elements
...
.specialClass element gets parsed/added to DOM
Listener triggers function on that element
...
.specialClass element gets parsed/added to DOM
Repeat as before
...
Page finishes rendering
So, is this possible at all? Thanks in advance.

Related

Check what part of JavaScript code is rendering a specific Html element?

On this page https://detail.1688.com/offer/548835845261.html
I want to scrape shipping price and shipping weight as highlighted in image.
I dont know how is this being loaded into DOM.
Its not included in DOM. Its not even loaded by an AJAX call.
Not sure whats going on.
The only relevent part I see on initial page source is
data-unit-config="{"calculationUrl":"https://laputa.1688.com/offer/ajax/CalculateFreight.do","freightTemplateId":"10342228","beginAmount":"1","unitWeight":"1","refPrice":"88.00","isCodOffer":""}"
I tried making GET request to that URL
https://laputa.1688.com/offer/ajax/CalculateFreight.do?freightTemplateId=10342228&beginAmount=1&unitWeight=1&refPrice&88.00&isCodOffer
but not luck.
I want to ask is there any way I can check what part of JavaScript code is rendering a specific Html element?
My recommendation would be:
When the element appears, inspect it.
In the Elements panel, find the first ancestor element to the mystery element that has no mysterious origins (an element that you know how it is getting on the page).
Right-click the ancestor element from step #2 and navigate and select Break on... ==> Subtree modification
Refresh the page _with the devtools on the Elements panel (seems to not work otherwise)
Now anytime JavaScript modifies descendants of the element you set the breakpoint on, the sources panel will pause and allow you to inspect what is going on.

Putting javascript inside HTML

I have two questions on placing JS inside HTML. I can't find where these two points were answered here.
Questions are basically
Let's say I put Javascript in the head, and say the script tries to get some HTML element by ID, and register a click handler for it. Now since, the HTML has not been loaded yet (because the script is called in head), will this work?
On the other hand, if register a function in head which gets called when DOM is loaded, and inside that function I put code which registers handler for click on some button, then as I understand user has chance to click button but the handler will not be called because the whole DOM was not loaded yet (and that is where the button click handler is registered). Am I correct?
What is the solution and best way to put Javascript in HTML considering above two points?
Let's say I put Javascript in the head, and say the script tries to
get some HTML element by ID, and register a click handler for it. Now
since, the HTML has not been loaded yet (because the script is called
in head), will this work?
No, this won't work. Whenever <script> tag is seen, javascript is executed right away before going further with DOM building. (if javascript is external, it will wait to fetch it over network and execute it) It will act upon the current snapshot of DOM if the javascript is using DOM APIs like getElementById. When script tries to get the element by id, it tries to get it from DOM, (All DOM APIs acts on DOM) which is being built right now. As the element is not yet added to DOM (we are processing head) it won't be able to get the element. As it cannot get the element it will throw error if you try to access addEventListener or onclick of null.
On the other hand, if register a function in head which gets called
when DOM is loaded, and inside that function I put code which
registers handler for click on some button, then as I understand user
has chance to click button but the handler will not be called because
the whole DOM was not loaded yet (and that is where the button click
handler is registered). Am I correct?
Yes, you are correct. DOM load generalises the condition that all elements are added to the DOM and is ready for any operation on any element defined by HTML.
Also, window.load will be triggered only after DOM is loaded and all the external resources like images, videos are also loaded. Using it can delay the event attachments further.
What if I want to add event listener immediately?
If you want to immediately bind an event you can do it with inline scripts, just after the element although usually not a requirement and neither a good practice.
<p>whatever</p>
<button id="mybutton">Click me</button>
<script>
document.getElementById('mybutton').addEventListener(...
</script>
<p>rest of HTML</p>
This also opens another possibility, if you put your scripts at the end of HTML instead of head, it makes sure that all elements are added to the DOM and DOM is practically ready for any operations using DOM APIs. This was used widely when listening to DOM load event was not that easy. Although I can't find the correct reference, this was a recommendation by Doug Crockford.

Move elements to iframe's parent with data and events

this is the situation.
I have a complex UI inside an iframe in which a user can perform several actions before submitting. During the process, the user can switch to another page (in the iframe) and come back. That means 2 postbacks inside the iframe.
Obviously, I don't want the user to lose everything but since all the actions have been done on the client-side, I can't reload the previous state from the server.
So now, I'm trying to move the first page content to the iframe parent and put it back in place when we come back. I'm half way there since the elements show back in the page but they lose their data attributes and event handlers.
I'm using this to move the content on the parent :
$("#resp").clone(true).attr("id", "refillResp").appendTo(window.top.$("#global"));
And this to put it back :
window.top.$("#global").find("#refillResp").clone(true).attr("id", "resp").appendTo($("#tdResp"));
Is there anyone who knows a way to do this ?
PS: I've tested how the content react when simply moved on the parent and data and events are already gone.
It is more a comment, but it does not fit in there.
Moving DOM element around different documents around is alway a little bit risky. Because it could lead to unexpected behavior (memory leaks, elements that behave strange). In current browsers it most of the time works, but i would not recommend it.
If you use jQuery you have another problem. jQuery has a cache for storing informations about data and event that are assigned to an Object, this cache is stored with the documents window. For further details please read this answer, to another question:
How to set jQuery data() on an iFrame body tag and retrieve it from inside the iFrame?
So if you move element with jQuery between different document, you will currently on the one hand loose these informations, on the other hand it could result in memory leaks.
With events it is even more complicate. e.g. if you have delegated events it could be become completely messy.
If you need to exchange data between iframe and parent you should think of some other logic.
I also mention this in a comment to the other answer of me, where i referred to this post:
How to set jQuery data() on an iFrame body tag and retrieve it from inside the iFrame?

Detect when HTML has finished loading AND rendering on the page

Is it possible to detect when HTML has finished loading AND rendering on the page? If so, how?
The page consists of HTML, calls to ajax, receives data back from ajax calls, lots of javascript stuff and some images too. I would like to detect when everything has finished. What I can't do is stick a function call at the end, because I don't know when the end is.
For example, the page has lots of ajax style elements which users can pick and choose from, i.e. user 1 might have a different number of elements than user 2 and even in a different order.
So what I can't do is use the last function to simulate the end of HTML rendering, because I won't know which function will be the last function.
The problem with answering your question is in the last few sentences where you say that you don't know where the end is because of user choices. There are two automatic events you can bind JavaScript to. What jQuery calls $(document).ready() which means the DOM is ready for manipulation (before images load and after external scripts and CSS are loaded) and what is more commonly called body onload (in jQuery this can be written as $(window).load()) which runs after all static assets are fetched. Your Ajax calls will all fire after at least the DOM is ready and there is no defined event for what happens after all of them. For that you need to keep track of them on your own. You could use a "last horse out of the barn" approach. Add a small bit of logic to the end of your Ajax calls to see if it is the last and raise a custom event called "reallyAllDone"
Yes onload
<body onload="load()">
That would detect when the contents of the body have loaded.
$(document).ready
will help you to know when the dom has finished its event and its ready

Remove all jQuery event handlers

I am creating a jQuery Mobile web app, which loads some pages.
For example, a.html is my main page. It may call b1.html,b2.html,...,b100.html (User may click on one of buttons). (The pages are loading with ajax navigation feature of jQuery Mobile)
And there is some events in each b[i].html page, and ids and many things are same in every b[i].html page. But the point is, at any time, just one of them may be in DOM. So there will be no id conflicts or such.
The problem
The problem is the conflict of the events. When user come back to a.html from b[i].html, the HTML will be removed, but events will remain. This will cause many problems if I first go to b[i].html, and then come back to a.html and then go to b[j].html. I mean, b[j].html will not work correctly... :(
What I have tried
I have putted this in a.html, to remove all events:
$("#mainpage").off("pagebeforeshow").on("pagebeforeshow",function() {
$("*").not("#mainpage").off();
//Other initialization codes...
});
But, problem not solved...
(mainpage is the id of data-role="page" of a.html)
Example
For example, I have this in each b[i].html:
$(window).resize(function () {
alert("Resized");
});
At the beginning (in a.html), If I resize the window, there will be no alerts, but after visiting b[i].html and then coming back to a.html, I'll see alerts if I resize the window, even with that line of code (What I have tried part.)...
So, How to remove those event handlers when users come back to a.html from b[i].html?
If you are using jQuery Mobile, more than one of said pages may exist in the dom at the same time, resulting in non-unique id conflicts.
I would ditch putting js on the individual pages and have it done from the primary page, or through a script loading system such as require.js. Then do all of the events through delegation from the document. Obviously that won't work with window.resize(), but it doesn't need to be delegated anyway.
"Can you please explain more?"
Basically, if you are including scripts on the child pages, you will need to have both setup and teardown for every page. setup adds the events, and teardown removes them. If you instead used a single global script that adds ALL of the events using event delegation from the document, all of the pages should work. Obviously that global script could get pretty big on a complex site, so you could instead use require.js to load in js that does the same thing as needed, preventing it from loading the same dependency more than once.
As far as removing all events, I've never tried this, but can you use $("*").off()? According to the docs it should work. I'm not sure how it will affect jQuery mobile. To remove events on the document and/or window, you will have to do it manually because $("*") will not select them.
$(document).on("vmousemove","#link",func) is how you delegate an event from the document.

Categories