I have div with vertical scroll bar. Div is being updated dynamically via ajax and html is inserted using jQuery's .html method.
After div is updated scroll bar returns to top and I am trying to keep it in the previous position.
This is how I'm trying it:
var scrollPos = $('div#some_id').scrollTop(); //remember scroll pos
$.ajax({...
success: function(data) {
$('div#some_id').html(data.html_content); //insert html content
$('div#some_id').scrollTop(scrollPos); //restore scroll pos
}
});
This fails. My best guess is that it is failing due to inserted html not rendered (ie. no scroll).
For example this works.
setTimeout(function(){
$('div#some_id').scrollTop(scrollPos);
}, 200);
But this is dirty hack in my opinion. I have no way of knowing that some browsers won't take more then these 200ms to render inserted content.
Is there a way to wait for browser to finish rendering inserted html before continuing ?
It's still a hack, and there really is no callback available for when the HTML is actually inserted and ready, but you could check if the elements in html_content is inserted every 200ms to make sure they really are ready etc.
Check the last element in the HTML from the ajax call:
var timer = setInterval(function(){
if ($("#lastElementFromAjaxID").length) {
$('div#some_id').scrollTop(scrollPos);
clearInterval(timer);
}
}, 200);
For a more advanced option you could probably do something like this without the interval, and bind it to DOMnodeInserted, and check if the last element is inserted.
I will just like to point out one difference here: One thing, is when the .html() have completed loading, but the browser actually render the content is something different. If the loaded content is somewhat complex, like tables, divs, css styling, images, etc - the rendering will complete somewhat later than all the dom ellements are present on the page. To check if everything is there, does not mean the rendering is complete. I have been looking for an answer to this by myself, as now I use the setTimeout function.
Such callback does not exists because .html() always works synchronously
If you are waiting for images loading, there's one approach https://github.com/desandro/imagesloaded
Related
I've created this code to print data from an iFrame
function (data) {
var frame = $("<iframe>", {
name: "iFrame",
class: "printFrame"
});
frame.appendTo("body");
var head = $("<head></head>");
_.each($("head link[rel=stylesheet]"), function (link) {
var csslink = $("<link/>", { rel: "stylesheet", href: $(link).prop("href") })
head.append(csslink);
;});
frame.contents().find("head")
.replaceWith(head);
frame.contents().find("body")
.append(this.html());
window.frames["iFrame"].focus();
window.frames["iFrame"].print();
}
This creates an iFrame, adds a head to where it sets all the css links that are needed for this website. Then it creates the body.
Trouble is, the styling won't get applied to the print, unless I break at line frame.contents().find("head").replaceWith(head), which means that something in that part is running asynchronously.
Question is, can I somehow get the code to wait for a short while before running that line, or is there perhaps another way to do this? Unfortunately I'm not all that familiar with iFrames, so I have no clue what it's trying to do there.
This turned out to be a real hassle. I've always been reluctant to using iframes, but since there are many resources saying that using an iframe for printing more stuff than what's on the screen, we figured we'd give it a try.
This was instead solved by putting the data inside a hidden div which then was shown before a window.print(). At the same time, all other elements on the page were given a "hidden-print" class which is a class we're already using to hide elements for prints.
This might not be as elegant for the user (The div will show briefly before the user exits the print dialogue), but it's a way more simpler code to use and manage.
I think you could / should move the last focus() and print() calls to a onload handler for the iframe, to get it to happen after styles are loaded and applied.
I've just run into the same issue and did the following:
setTimeout(() => {
window.frames["iFrame"].focus();
window.frames["iFrame"].print();
}, 500)
This appears to have sorted it for me. I hate using timeout and the length is guess work at best but as it's not system critical it's something I can run with for now.
There is a single Page Application in AngularJS.
It has nested tabs. In the inner tab there is a button on which some event gets fired.I need to trigger the click event of this button present on the inner tab Button gets rendered after both the tabs are rendered. What is the best way to wait until the tabs render themselves and the button is available.
I tried using 'while loop'(i.e keep looping until id for button is undefined) and $timeout(set timeout to 2-3 seconds) service but both have their consequences when there is delay in tab render.
Please suggest if there exists a better approach.
Even though this question is really old, I've found a solution that works for me and I think it might be helpful for some people.
Calling element.getBoundingClientRect() before executing further code worked for me. According to docs this method returns information about the position relative to the viewport (docs):
The Element.getBoundingClientRect() method returns a DOMRect object providing information about the size of an element and its position relative to the viewport.
Assuming that the screen has to render to find information about the position of an element, this function would technically wait for the html element to render or even make the html element render.
Remember, this is only an assumation and I can't guarantee that it works.
Things have changed quite a bit since this question was asked, so there's now a much nicer solution.
requestAnimationFrame(() => {
// this will be called just before the next video frame.
// That will be after any changes to the DOM have been completed.
});
The docs are here...
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
You can do it with jQuery:
$(document).ready(callbackFn);
or native JS:
document.addEventListener('readystatechange', function onReadyStateChange() {
if (document.readyState !== "complete")
return;
callbackFn();
}, false);
If in a web page I load some content after the initial page load and DOM ready event, when are the inserted nodes "ready", in the sense of having their sizes computed, taking CSS rules into consideration and all?
$.ajax({
url: ajaxUrl,
success: function (data) {
var page = $(data).find('.page').first();
page.appendTo(pages_container);
// if I try to get the width of an element here, I get 0
console.log(page.width()); // -> 0
// but if I do it, let's say 500ms later, now it is computed
setTimeout(function () {
console.log(page.width()); // -> correct width, non zero
}, 500);
}
});
Is there any kind of event, like "on inserted dom ready" or something to be able to execute a function after the sizes/layout of the ajax inserted content has been computed?
Your problem should not occur as you outline. Appending elements into a page is not an asynchronous operation. Javascript DOM manipulation is not multi-threaded. It simply returns after the DOM changes have been applied.
The DOM should be ready to use as soon as your append completes (including size properties):
page.appendTo(pages_container);
The usual cause is accessing the properties before the jQuery object is inserted, or that the new jQuery object is visually hidden and revealed later (fadein etc), but I see no transitions in your code.
Questions/thoughts:
Can you mock up a JSFiddle to replicate your problem?
Also check for other plugins etc that could modify your DOM after insertion.
Is there JS code in the inserted page?
Also what browser did this occur with?
I've been attempting to make an infinite (well semi-infinite) scroll that feeds itself from hard-coded divs (because I don't know any back-end languages yet). My research has turned up a ton of great jQuery for infinite scrolls, but they are all meant to be used with a database, not hard-coded content.
What I'm trying to achieve, ultimately, is an infinite scroll that starts by loading X div into the DOM, and as the user reaches the bottom of the page, loads X more divs, and repeats until no more divs are left to load.
Do any of you know of some good or relevant scripts or any fiddles that may help me? Part of my issue is that I'm still in that learning curve of JS; I often understand what's going on when I look at a script but I still have a hard time writing my own from scratch.
Any help or direction is appreciated. Thanks in advance!!
Based on the code here: Alert using Jquery when Scroll to end of Page
Create a new <div> element when you reach the bottom of the page
$(window).scroll(function() {
if (document.documentElement.clientHeight + $(document).scrollTop() >= document.body.offsetHeight)
{
$('body').append('<div></div>');
}
});
Your HTML needs to be stored somewhere and if you have enough of it to think about infinite scrolling you probably don’t want to load it all with the initial request, so let’s say each “post” is stored in an individual HTML file on your server: /posts/1.html. And you want to append those to a div in your main document: <div id="posts"></div>.
You need a method to download a given post and append it to your div:
function loadPost (id) {
$.get('/posts/'+id+'.html').done(function (html) {
$('#posts').append(html);
});
}
Now you need a way to trigger loadPost() when you scroll to the bottom of the page. This example uses jQuery Waypoints to call a handler function when the bottom of the div comes into view:
var currentPost = 1;
$('#posts').waypoint({
offset: 'bottom-in-view',
handler: function () {
loadPost(currentPost++);
}
});
I have the following problem: on a customer's homepage the navibar is loaded by javascript, but I need to change some URL's on it. If I just start my script on $(document).ready() it runs before the customers script and has no effect. I only can use setTimeout for my function to wait until the other script is ready, but it's not good or safe at all. I can't change anything on the website, only add a javascript - is there a way to time it after the other one?
You can use repeated setTimeout, in order to check if menu is accessible.
function check_menu(){
if(document.getElementById('my_menu')==null){
setTimeout('check_menu()',500);
} else {
//do some stuff
}
}
If you have information about the menu like the id or class, use the onLoad() jQuery method on the element. For example if the code is loading asynchronously, and you add the onload to one of the last elements it should fire after the content has finished.
$.post('AsyncCodeLoad.php', function(data) {
$('#lastElementToLoad').onLoad(RunMyFunction);
});
Or if you have no chance to insert your code into the async loading just add to the bottom of the </body>:
$('#lastElementToLoad').onLoad(RunMyFunction);
Just a thought.
Yes, add your script at the bottom of the <body /> tag to ensure it does not run until all other scripts have run. This will only work however if your customer is loading the nav links synchronously.
If the nav is being loaded asynchronously, use JS's setInterval to repeatedly check the contents of the nav for links. When you determine the links have been added, cancel your interval check and call your script's logic entry point.
Cheers
Fiddle: http://jsfiddle.net/iambriansreed/xSzjA/
JavaScript
var
menu_fix = function(){
var menu = $('#menu');
if(menu.length == 0) return;
clearInterval(menu_fix_int);
$('a', menu).text('Google Search').attr('href','http://google.com');
},
menu_fix_int = setInterval(menu_fix, 100);
HTML
<div id="menu">Bing Search</div>