I am writing a document reading application using Node-Webkit. A document can be hundreds of pages long and the interface allows opening the document to a specific chapter. Once the initial chapter is displayed, I want to load the rest of the book asynchronously. Since I don't know what direction the reader will scroll, my strategy is to alternately load chapters before and then after the initial chapter:
load chapter x -> load chapter x-1 -> load chapter x+1 -> load x-2 -> load x+2 ...
I am displaying the book in a containing div element with each chapter being a div within the container. I'm using jquery .before() and .after() to insert each chapter as it is fetched.
My problem is that the browser automatically scrolls up when I add chapters earlier in the book. So if I start with chapter 10, the browser scrolls up when I add chapter 9 and again with chapter 8 and so forth.
One (unsatisfactory) solution to the problem is to make an anchor tag for each chapter and store the id of the first chapter loaded. Then after each chapter is fetched, runing the code:
window.location.href = #originalChapter
keeps the initial chapter in the browser viewport. Of course the problem with that is that the reader cannot scroll while the rest of the book is being loaded.
Ideally, I would like to disable scrolling, load a chapter and then re-enable until the next chapter is fetched. I'm so far unable to figure out how to do that.
If you're updating the DOM from the click handler of an anchor (<a/>) tag, make sure you return false from the callback function.
The simple solution in my mind would be to not have the elements in the DOM until they are loaded. The user won't be able to scroll because there won't be content to scroll to. Then, it's just a matter of preserving the viewport when the elements are added.
Take the initial position of your scroll container and the height of the overflow container:
var scrollTop = $scroll.scrollTop(),
height = $container.height();
and use them to preserve the viewport when you prepend new elements. You don't have to do anything when you're doing the append operation.
var newHeight = $container.height(),
newScrollTop = scrollTop + newHeight - height;
$scroll.scrollTop(newScrollTop);
Here's a quick example: http://jsfiddle.net/Wexcode/tfszaocz/
Related
Here is the things I want to achieve with jQuery.
First of all, I want to save a copy of the entire page as is in a variable.
I then want to replace the currently loaded page with a loading page from another html file or URL without redirecting to that page.
After it has done loading, I want to replace the page back with what it originally was (the variable from step 1).
The question asked
At it's simplest (and this is a gross simplification that will lose state like event handlers, iframe state, shadow DOM etc.) you "save a copy" of the page's HTML by using .html() on the root element (html or body for example).
You can replace the entire top level element with fixed content from another HTML file or URL by providing the data to the same .html(data) function as well.
You can restore the original content by repeating step 2 using the data you saved off in step 1.
// Step 1 - save old page
const oldPage = $('html').html();
// result of fetch() or fixed HTML from file
// const newHTML = ...
// Step 2 - load new page
$('html').html(newHTML);
// Step 3 - restore old page
$('html').html(oldPage);
What you might actually want
If you only care that the user can't see the old page, you may want to wrap all of the UI inside a div that you can set display property to none on. You could also just overlay the new UI elements on top of the old one, possibly wrapped in a container with a solid background (to prevent old UI elements from being shown through transparent background).
I'm trying to inject some css to the body of my SharePoint webpage for a background image. The .js is essentially this:
document.getElementsByTagName( "Body" )[ 0 ].style.backgroundImage = <My Image URL>
document.getElementsByTagName( "Body" )[ 0 ].style.backgroundSize = "50%"
document.getElementsByTagName( "Body" )[ 0 ].style.backgroundPosition = "50% 50%"
And it works as I expect, unless there's an iFrame in the middle (or any other 'body' tag). Then the image gets put inside the iFrame as well as the body of my page. These iFrames are essentially popups that come up whenever you need to upload documents, change some settings on the site, etc. They're not up all the time but I don't want the image there, regardless.
When I call document.getElementsByTagName('Body') I always get the Body tag of whatever iFrame is currently up, or I'll get the document's main body. But it's always an HtmlCollection array of 1 item. Same thing happens if I use document.body
All of the body tags have the same generic setup ( <body class="ms-backgroundImage" style="..." spellcheck="false"> ) regardless of if they're the site's actual Body tag or if it's the iFrame's body.
Is there a way with Javascript to say "Apply to the main body, but not to any others"?
Your description contradicts the documentation (see https://developer.mozilla.org/en-US/docs/Web/API/Document/body). Are you really sure it behaves as you described? Your described behavior is nothing I have ever experienced as a developer.
Can you check again and maybe updates this question?
First, the reason you're getting an HtmlCollection when calling getElementsByTagName is because that function returns an array regardless of whether it matches zero, one or many items (observe the plural "s" in "Elements").
The different JS web api "get"-functions are pretty self explanatory that way, in that
getElementById (no plural s) returns a single DOM-node, whereas getElementsBy[Name|ClassName|TagName] all return an array of nodes.
Now to your main question:
By iFrames I take it you mean modal dialogs?
Modal dialogs in SharePoint have one thing in common, which is that they all have the url parameter "&IsDlg=1". This means you can check whether you're in a dialog or not, and have your code act accordingly.
Here's the code you're looking for, which will apply your style to all non-modal pages.
// could have used GetUrlKeyValue("IsDlg") === "1",
// but not sure if sp.js is loaded, so this is safer
var isDialog = document.location.search.indexOf("IsDlg=1") > -1;
// for non-dialog pages
if(!isDialog) {
var docBody = document.body;
// if for some crazy reason document.body returns an array??
if(document.body.length) { docBody = document.body[0] }
// styling
docBody.style.backgroundImage = "[Your Image URL]";
docBody.style.backgroundSize = "50%";
docBody.style.backgroundPosition = "50% 50%";
}
PS: Customizing the master page is not reccomended practice in newer (>2007) versions of SharePoint.
There are several other methods (CustomActions, for instance) of customizing the platform without touching the out-of-the box master page, which you should familiarize yourself with if you want to work professionally with sharepoint.
To make the change I was trying to do, I just had to add an image to the "Look and Feel" of the page via Site Settings -> Look and Feel. Quick and easy, doesn't require any code... Thankfully.
I need to scroll an inner window (i.e. div). I found this web site
How to scroll to an element inside a div?
but this shows how to do Javascript. I am calling the executor but I don't think I can pass values back to java (height, etc). This is my code section. Basically there is a div window with a bunch of elements. Some are hidden on the bottom and do not actually appear in the html unless scrolled to. Once they are scrolled to I believe the will remain there. So I figured I could just scroll a large number and it would not give an error if it were too much, it would just scroll as much as possible.
I scroll to the top and then done, like this:
String sid = rolesScroller.getAttribute("id");
js.executeScript("document.getElementById('" + sid + "').scrollTop -= 1000");
js.executeScript("document.getElementById('" + sid + "').scrollTop += 1000");
waitForXPathVisibility("Scroll", ROLES_SCROLLER_X);
will this be ok or do I need to somehow figure the exact amount to scroll and scroll just by that amount?
I see there is an element.scrollHeight. Does the possible values for scrollTop go from 0 to scrollHeight? Are the units both in pixels?
The elements in the divs are themselves nodes (list values) which can also be expanded creating more elements underneath. Every time I search for a value I have to do the above to make sure everything is in view. The way I have it now works to an extent. But sometimes after scrolling when I try to access a node I get a StaleElementException. However if I do a waitForStaleElement() it sometimes gives an error saying the element did not go stale. Is there a way after executing the javascript that you can make sure all actions have completed so that a stale element won't happen?
To Summarize
When using the javascript executor from Java/Selenium is there a way to pass the javascript variables back to java so they can be used in later jasascript executor commands? (if that example above of -1000 +1000 is OK then this does not matter).
How can you ensure that the javascript command has completed before continuing so when you try to access an element in the scrolled div you will not get a stale element (I tried examining one element in the div, and the div itself).
It seems that the container is dynamically constructed upon scrolling. Try to scroll the last element at the top with scrollIntoView and then wait for a different element at the end:
WebDriverWait wait = new WebDriverWait(driver, 5);
JavascriptExecutor jse = (JavascriptExecutor)driver;
// get the last element
By lastChild = By.cssSelector("#list > div:last-of-type");
WebDriver elem = driver.findElement(lastChild);
// scroll the last element at the top
jse.executeScript("arguments[0].scrollIntoView(true);", elem);
// wait for a new element at the end
wait.until((WebDriver drv) -> !elem.equals(drv.findElement(lastChild)))
I'm building a page that has ul#tiles and li.tile.
The default filtering for the page view is filter by all(presents every tile).
When I filter the list I still have the extra space and scroller for the rest of the page - extra blank page.
I want to remove the extra space if the current filter presents less views than the filter before it.
The resize event does fire and resizeFunction() is being called but the screen isn't changing.
function resizeFunction() {
var tilesHeight = $('#tiles').height();
window.resizeTo(window.screen.availWidth, tilesHeight + 50);
};
$(window).resize(function() {
resizeFunction();
});
Window targets the brwoser. To get rid of blank space you will have to target the DOM element that contains your tiles.
Also make sure no height constrains are present on any parent element. This might even make any resizing function obsolete.
I am fetching an attribute of a div from a page which loads the elements dynamically upon scrolling to the bottom of the page. There are 10 div's on the page initially and upon scrolling 10 new div's are loaded each time dynamically to the page.
My script looks like this:
var arr = document.getElementsByTagName('html')[0].innerHTML.match(/data-username="([^"]*")/gm); // I want the value from the attribute data-username
alert(arr.length); // size is 10
window.scrollTo(0, document.body.scrollHeight); // scroll to bottom to load 10 divs dynamically
arr = document.getElementsByTagName('html')[0].innerHTML.match(/data-username="([^"]*")/gm);
alert(arr.length); // size is still 10 but should be 20
Why is the array size still 10? How can I get all the 20 divs and iterate through them?
I took a look at the HTML source code and the problem is that the dynamically loaded div's are not there... How am I supposed to get the dynamically loaded elements?
I assume you are doing an ajax call to get the additional data, and you should wait for those elements to be available in the DOM (once it's complete) before doing things with them. That's quite easy with jQuery (as you had that as a tag)
http://api.jquery.com/load/
http://api.jquery.com/jQuery.ajax/