0

I'm writing an online book reader. The average book has 270 distinct pages. I'd like the user to be able to browse through the pages but if all are loaded immediately then the page freezes as it's too much content. To fix this I think the best solution is that each page's content is display:none to begin with and only becomes display:block when in view.

Unfortunately I'm not very experienced with Javascript and so I'm having difficulty figuring out how to do this.

I'm using only raw Javascript (no JQuery). So far I have this:

https://jsfiddle.net/ya2n8bfm/

<html>
<head>
<style>.bookzone { margin:0; padding:0; overflow:scroll; }</style>
<script>
    function scollPos() {
        var sp = document.getElementById("bookzone").scrollTop;
        if (sp > 100) {
            document.getElementById("p2").style.display = "block";
        }
    }
</script>
</head>
<body>
<div class="bookzone" onscroll="scollPos();">
    <div style="width:400;height:400;"><div id="p1" style="display:none;">Page 1</div></div>
    <div style="width:400;height:400;"><div id="p2" style="display:none;">Page 2</div></div>
    <div style="width:400;height:400;"><div id="p3" style="display:none;">Page 3</div></div>
    <div style="width:400;height:400;"><div id="p4" style="display:none;">Page 4</div></div>
    <div style="width:400;height:400;"><div id="p5" style="display:none;">Page 5</div></div>
    <div style="width:400;height:400;"><div id="p6" style="display:none;">Page 6</div></div>
    <div style="width:400;height:400;"><div id="p7" style="display:none;">Page 7</div></div>
    <div style="width:400;height:400;"><div id="p8" style="display:none;">Page 8</div></div>
    <div style="width:400;height:400;"><div id="p9" style="display:none;">Page 9</div></div>
    <div style="width:400;height:400;"><div id="p10" style="display:none;">Page 10</div></div>
    <div style="width:400;height:400;"><div id="p11" style="display:none;">Page 11</div></div>
    <div style="width:400;height:400;"><div id="p12" style="display:none;">Page 12</div></div>
</div>
</body></html>

That doesn't actually seem to work at all. I'd like that each page becomes display:block when it's in the viewport (any part of it) and becomes display:none when it's entirely outside of the viewport.

So my two issues are: (1) what I've done so far doesn't seem to work at all, (2) I'm not sure how to make it know when an element is in the viewport. I could do (2) based on a whole series of rules since every page is the same height, but perhaps there is a more efficient way?

Alasdair
  • 13,348
  • 18
  • 82
  • 138
  • hiding element still need to be loaded. so the idea is wrong. instead use lazy loading technology. – Bhojendra Rauniyar Nov 09 '15 at 10:18
  • It doesn't matter if it's loaded, only that it's not rendered. The problem is rendering. I already tested the page load if all divs are set to `display:none` and it works fine with no freezing despite there being over 100,000 lines of HTML. Also I'm pretty sure images are not even loaded if they start on `display:none`. – Alasdair Nov 09 '15 at 10:19
  • Also you have to specify unit of measure for `width` and `height` properties. – Alex Char Nov 09 '15 at 10:20
  • Have you looked into Ajax at all? I have a feeling it'd be beneficial to try and avoid sending this over to the client in one go, whether it's hidden or not. – OliverRadini Nov 09 '15 at 10:25
  • Yes I've looked at Ajax, but whether I use that or not this part is still the same. – Alasdair Nov 09 '15 at 10:26
  • 1) Load 1GB video in a div and make it display:none 2) Load 1GB video in a div and make it display:block? what is the difference you are obtaining in the performance. For your scenario, lazy loading is a solution. Not loading all pages at once. CSS is just a client side rendering part and does not affect the performance – schoolcoder Nov 09 '15 at 10:29
  • @cobra, if you load a 1GB video in a div with `display:none` most browsers do not load the video at all, they will begin to load it only when it becomes `display:block`. Anyway, I already tested this, it's not an issue. My idea will work if I can make it actually change the state. – Alasdair Nov 09 '15 at 10:33

4 Answers4

0

http://jscroll.com/ could do the job for you? The example they give on the home page looks very similar to what you want.

c.early
  • 186
  • 9
  • Thanks, but that's not the same thing. What I need has a fixed overall size and can be scrolled up and down with out-of-view content always disappearing. – Alasdair Nov 09 '15 at 10:32
0

There's a good example of selecting items within a viewport here:

How to get on-screen visible element objects in jQuery?

Basically what you'll want to do is hide all your panels by default apart from what is visible in the viewport when the page loads, using the instructions in that link you can then append an active class to the elements within the viewport as you scroll down the page and add display:block to them.

Keep in mind most solutions will actually load a few elements ahead to make the experience a bit better, I'm not sure if this demo does that or not.

Community
  • 1
  • 1
Tim Sheehan
  • 3,994
  • 1
  • 15
  • 18
  • I'm specifically not using JQuery, so unfortunately this does not help me. – Alasdair Nov 09 '15 at 10:36
  • I don't mean to be an ass, but if you're struggling already and trying to do this with JS alone and no help from 3rd party libraries you're gonna have a bad day... – Tim Sheehan Nov 09 '15 at 10:40
  • I understand your point, but I'd prefer efficiency forever for all users over one day of struggle for me. – Alasdair Nov 09 '15 at 10:43
  • @Alasdair Just because you're using vanilla javascript doesn't mean it'll be more efficient, there's more to efficiency than the overheads of jQuery. – Tim Sheehan Nov 09 '15 at 10:49
  • That depends how I write it. I prefer to use 10 lines of code instead of 10,000 where possible. – Alasdair Nov 09 '15 at 11:53
0

The scroll appears on the window and not on the "bookzone" div.

You need to set a height/maximum-height on the bookzone div in order to have scroll on it. You also need to add the "bookzone" id.

This appears to work:

<html>
<head>
<style>.bookzone { margin:0; padding:0; overflow:scroll; }</style>
<script>
    function scollPos() {
        var sp = document.getElementById("bookzone").scrollTop;
        if (sp > 100) {
            document.getElementById("p2").style.display = "block";
        }
    }
</script>
</head>
<body>
<div id="bookzone" class="bookzone" onscroll="scollPos();" style="max-height:400px">
    <div style="width:400;height:400;"><div id="p1" style="display:none;">Page 1</div></div>
    <div style="width:400;height:400;"><div id="p2" style="display:none;">Page 2</div></div>
    <div style="width:400;height:400;"><div id="p3" style="display:none;">Page 3</div></div>
    <div style="width:400;height:400;"><div id="p4" style="display:none;">Page 4</div></div>
    <div style="width:400;height:400;"><div id="p5" style="display:none;">Page 5</div></div>
    <div style="width:400;height:400;"><div id="p6" style="display:none;">Page 6</div></div>
    <div style="width:400;height:400;"><div id="p7" style="display:none;">Page 7</div></div>
    <div style="width:400;height:400;"><div id="p8" style="display:none;">Page 8</div></div>
    <div style="width:400;height:400;"><div id="p9" style="display:none;">Page 9</div></div>
    <div style="width:400;height:400;"><div id="p10" style="display:none;">Page 10</div></div>
    <div style="width:400;height:400;"><div id="p11" style="display:none;">Page 11</div></div>
    <div style="width:400;height:400;"><div id="p12" style="display:none;">Page 12</div></div>
</div>
</body></html>

But the best solution would be a "lazy loading"/"infinite scroll" solution. This will likely mean that you will need to use a library/plugin.

  • Hi, I can see this works if I save it as HTML and open in it Chrome, but it doesn't appear to work on JSFiddle: https://jsfiddle.net/ya2n8bfm/1/ Any idea why that would be? – Alasdair Nov 09 '15 at 11:52
  • Yes, it seems you need to add the "scollpos" function into global scope in order to work: scrollPos = function () { ... } – Gabriel Deliu Nov 09 '15 at 13:10
0

I figured it out.

<html>
<head>
<style>
.bookzone { margin:0; padding:0; overflow:scroll; }
.outOfView { display:none; }
.inView { display:block; }
</style>
</head>
<body>
<div id="bookzone" class="bookzone" style="max-height:400px">
    <div style="width:400px;height:400px;"><div id="p0" class="outOfView">Page 1</div></div>
    <div style="width:400px;height:400px;"><div id="p1" class="outOfView">Page 2</div></div>
    <div style="width:400px;height:400px;"><div id="p2" class="outOfView">Page 3</div></div>
    <div style="width:400px;height:400px;"><div id="p3" class="outOfView">Page 4</div></div>
    <div style="width:400px;height:400px;"><div id="p4" class="outOfView">Page 5</div></div>
    <div style="width:400px;height:400px;"><div id="p5" class="outOfView">Page 6</div></div>
    <div style="width:400px;height:400px;"><div id="p6" class="outOfView">Page 7</div></div>
    <div style="width:400px;height:400px;"><div id="p7" class="outOfView">Page 8</div></div>
    <div style="width:400px;height:400px;"><div id="p8" class="outOfView">Page 9</div></div>
    <div style="width:400px;height:400px;"><div id="p9" class="outOfView">Page 10</div></div>
    <div style="width:400px;height:400px;"><div id="p10" class="outOfView">Page 11</div></div>
    <div style="width:400px;height:400px;"><div id="p11" class="outOfView">Page 12</div></div>
</div>
<script>
var bz = document.getElementById('bookzone')
if (bz.addEventListener) {
    bz.addEventListener('scroll', scrollPage);
} else {
    bz.attachEvent("onscroll", scrollPage);
}
var vis, a, b;
function scrollPage() {
        vis = Math.floor(document.getElementById("bookzone").scrollTop / 400);
        if (a) { a.className = 'outOfView'; }
        if (b) { b.className = 'outOfView'; }
        a = document.getElementById('p' + vis)
        b = document.getElementById('p' + (vis + 1))
        a.className = 'inView';
        if (b) { b.className = 'inView'; }
    }
scrollPage();
</script>
</body></html>
Alasdair
  • 13,348
  • 18
  • 82
  • 138