Pause jQuery ready events until multiple HTML DOM updates are finished - javascript

I am using jQuery to make AJAX submits and receive JSON replies containing multiple DOM IDs and corresponding HTML fragments that I need to update. Thus, I do multiple jQuery.html calls like $('#id1').html('...'); $('#id2').html('...');.
For every fragment containing a ready handler like $(function(){...});, the event is immediately triggered. I'd prefer to call it once after all updates were made.
Is there any way to do that?
From what I've read so far, a jQuery.trigger() could be used to manually trigger the ready event. But I'd need to save and restore the current handlers or postpone event delivery by overwriting the jQuery internals somehow. The latter is something that I'd like to avoid, maybe there's some best practice I don't know yet?

Have a look at the holdReady function.
See: http://api.jquery.com/jQuery.holdReady/

I would start by moving the js you have in your views into external js files where they can be invoked from the ajax callback functions.
As you are finding out in-page ready events can soon leave you feeling out of control and become very hard to maintain. Regain control, namespace your js up into logical sections and lose the ready reliance.
There is no need for ready statements if you include your js before the closing body tag. The best advice I can give is to start being explicit in your apps lifecycle about what js gets called when, rather than relying on the well over abused ready statement.
Rebecca Murphy has written some very good articles/presentations about this.

Related

importing vanilla js to React is causing event listeners to duplicate

I'm integrating an HTML/CSS/JS template on a React SPA.
JS script (functions.js) uses jQuery to add event listeners when the script loads on multiple tags dynamically ($(p).addClass('.center'))
In order for it to work, the script must be added using useEffect for it to add the listeners when the component finishes rendering (in every component). Doing that, changing the route will replicate the event listeners already there for the parent component, thus, creating lag and unexpected behaviors in the app.
Here's what I have tried so far:
Having the script in index.html doesn't solve the problem. index.html loads only once, changing the route doesn't execute the script again.
Loading the script when the route changes add more duplicated event listeners since you can't destroy the old ones when the component dismounts.
Some advice on approaches I should try can help me solve the problem.
This is not recommended, and maybe not possible.
You should not manipulate the DOM from outside of React (including adding event handlers).
Anyway, you can add an extra script to a React-App, but you can not remove the script again. If this extra script is adding event handlers (and doesn't remove them),
then you can not remove the event handlers.
(You can not remove an added script because adding an extra script means
basically to execute some extra Javascript code, which can do whatever
it wants, including adding variables and functions, and even modify existing objects. You can not simply undo that.)
solutions (or not)
To achieve what you want you probably need to modify the imported script:
You can make the imported script manage the event handlers (i.e. removing them), or
you can give React the control over these event handlers (i.e. re-implement them in React),
and e.g. only keep the callbacks inside the imported script.
(Furthermore, you should make sure the script is only loaded once.)
Probably bad ideas:
If you can not remove event handlers, you may be able to remove the DOM nodes where the event handlers are attached to.
But working with the same DOM from React and from a separate script is likely to cause problems.
One might think of extracting the components that need the event handlers, and destroy them and recreate
them everytime you need a new event handler. But that sounds quite dirty to me,
and I would expect any kind of other errors later, including possibly memory leaks.
You would still re-load the same script file over and over again, only to attach a different event handler.
Old references to the DOM nodes might still stay in memory.

Notification when function is registered on e.g. $(document).ready()

is there a way to be notified when a function is registered via the jQuery $(document)ready() functionality and get a reference to that?
The background:
Im using a parent theme on a wordpress site, which uses ajax page transitions and document ready is only called on the first load. Now i want a reference to each function previously registered to call them again if my page changes.
The goal: is to restore the functionality of $(document).ready() as not only me, but many other plugins out there are using it and i obviously dont want to rewrite them all.
Yes, i could call MY registered function with no efford but this feels kind of lackluster while destroying the underlying functionality. By the way: it is the parent themes transitions, so overriding this wouldn't be the best solution either.
What i want is to provide an addition, that intercepts every registration and calls the registered functions again manually after the transition. Is that a good idea?
(notice calling ready() manually doesn't work if it was called already automatically on the initial page load)
why dont you wrap them all in a onPageChanged function and call that on document ready, and in the success handler from ajax calls

jQuery selectors not working after ASP.NET postback (no UpdatePanels, not AJAX)

I need to make a change to an old page as quickly as possible, and the effort to AJAX-ify it in order to obviate postbacks would take too long (making a more correct version of it will have to come later, this new functionality needs to be in place ASAP). The javscript changes required are too complicated to attempt entirely in the plain JS the page currently uses for everything (it's enough of a mess as is), so I decided to implement the new functionality quickly using jQuery.
Everything works fine until there's a postback, after which the document ready function still runs, but the selectors no longer find anything. I'm not using ASP.NET AJAX, so there's no UpdatePanels or partial postbacks.
What's happening and how can I fix it in the simplest, fastest possible way?
While $(document).ready() is ideal for one-time initialization routines, it leaves you hanging if you have code that needs to be re-run after every partial postback. Of course there are ways to get around this. But can you try using .NET franeworks pageLoad() and bind your events there and see if selectors still work after postback.
<script type="text/javascript">
function pageLoad() {
$("#Button1").on('click', function () {
...
});
}
</script>
If you have a a trigger attached to the DOM, and that element in the DOM gets replaced, the trigger will be lost. Such a trigger might look like $('#mydiv').on('click', function(){});.
Instead, you have the attach the trigger to a DOM element that wont be replaced. The easy way is to attach this to the document, but you'd be recommended to narrow the search.
Such a selector would look like
$('document').on('click', '#mydiv', function() {});
This means that if the element #mydiv gets recreated, then the trigger is not lost.
You can read more about delegated events at http://api.jquery.com/on/

Avoiding a freeze when executing all javascript functions onDomReady / onDomLoaded

So I want to migrate all my javascript functions to requireJS and will try to use as much as possible the ondomready event. BUT:
this will freeze the browser, since all javascript is synchronously. This is bad. I mean wow the user sees the browser content a bit faster, but is going to try to click somewhere just to realize the browser was frozen, and has to click again. This is very bad. Is there a way around this?
Patient: It hurts when I do this.
Doctor: Then don't do that.
If you see freezing on the dom ready event then perhaps you are trying to do too much. Executing javascript is quick. Doing a page redraw is slow.
Rather than lots of small events that each make changes to the dom and each cause a page redraw you should have one function that processes a list of changes that need to be made. This is what the domReady plugin does before the ready event. After the ready event it just runs them as it receives it which could cause multiple redraws.
I learnt this while writing my own animation library. I was using individual setInterval()'s to change a single property. When doing more that four a once the animation was no longer smooth. A better way to do this is a single interval that processes a list of changes that need to be made.
Edit:
Instead of using domReady as a plugin require(["domReady!"], use it as a module so that you can run initialisation code straight away then make changes to the dom later.
require(["domReady"], function(domReady) {
var element = document.createElement('table');
//more setup code
domReady(function(){
document.body.appendChild(element);
});
});

Why focus an input on page load instead of inline?

Almost all web pages that I see designed to set the focus to an input box add the code into a body onload event. This causes the code to execute once the entire html document has loaded. In theory, this seems like good practice.
However, in my experience, what this usually causes is double work on the user, as they have already entered data into two or three fields and are typing into another when their cursor is jumped back without their knowledge. I've seen a staggering number of users type the last 2/3 of their password into the beginning of a username field. As such, I've always placed the JS focus code immediately after the input to insure there is no delay.
My question is: Is there any technical reason not to place this focus code inline? Is there an advantage to calling it at the end of the page, or within an onload event? I'm curious why it has become common practice considering the obvious practical drawbacks.
A couple thoughts:
I would use a framework like jQuery and have this type of code run on $(document).ready(.... window.onload doesn't run until everything on the page is fully loaded, which explains the delay you have experienced. $(document).ready(... runs when jQuery determines the DOM has been loaded. You could probably write the same sort of logic without jQuery, but it varies by browser.
I prefer to keep my Javascript separate from my HTML because it allows for a cleaner separation of concerns. Then your behavior is then kept separate from your document structure which is separate from your presentation in your CSS. This also allows you to more easily re-use logic and maintain that code — possibly across projects.
Google and Yahoo both suggest placing scripts at the bottom of the html page for performance reasons.
The Yahoo article: http://developer.yahoo.com/performance/rules.html#js_bottom
You should definitely place the script in the appropriate place if it means the correct user experience -- in fact I would load that part of the script (Used for tabbing inputs) before the inputs to ensure it always works no matter how slow the connection.
The "document.ready" function allows you to ensure the elements you want to reference are in the dom and fires right when your whole document dom is loaded (This does not mean images are fully loaded).
If you want you could have the inputs start out as disabled and then reenable them on document ready. This would handle the rare case the script is not ready yet when the inputs are displayed.
Well if you call it before whole page has loaded you really don't know if the element already has been loaded when you make your call. And if you make your call in pre-hand you should check if the element really exists even if you know it always should.
Then to make the call inline, which might seem ideal. But on the other hand it's really bad if a page takes that long to load that you can make several inputs during the loading phase.
Also you could check if input has been made etc.
Also it is possible to check if any input on page contains focus if($("input::focus, textarea::focus").length)... and otherwise set focus on desired input.
Use the autofocus HTML attribute to specify which element should initially receive focus. This decouples JavaScript and gracefully degrades in older browsers.

Categories

Resources