1

I'm practicing by making this website - https://backstagetalks.com/ I created divs that are scrollable and made it such that each generates a random color.

However I want to know how to use Javascript to identify when a particular div has snapped to the center so that it can set the background color to match the color of that div

I tried to use the scrollX, scrollY and offSetTop, offsetHeight properties. But the divs all have fixed offsetHeight, so it didn't work

I don't know jquery yet and I haven't started learning CSS or JS libraries/frameworks, so I don't know how to use those to help.

This is the Code:

const gra = function (min, max) {
    return Math.random() * (max - min) + min;
};

const init = function () {
    let items = document.querySelectorAll(".scroller div");
    for (let i = 0; i < items.length; i++) {

        items[i].style.minHeight = gra(100, 100) + "vh";

        const randomColor = Math.floor(Math.random() * 16777215).toString(16);
        items[i].style.backgroundColor = "#" + randomColor;
        let itemColor = items[i].style.backgroundColor;
        // items[i].style.borderColor = "#" + ((1 << 24) * Math.random() | 0).toString(16);
    }
}
init();
@import url('https://fonts.googleapis.com/css2?family=Bangers&family=Chivo+Mono:ital,wght@1,500&family=Hanalei+Fill&family=Fredoka+One&display=swap');

html, body{
    margin: 0;
    /* font-family: Arial, Helvetica, sans-serif; */
}
::-webkit-scrollbar {
    display: none;
}

header {
    padding: 0.7em 0 0 0 ;
    color: red;
    font-family: 'Bangers';
    font-size: 25px;
}

header h2 {
    margin: 0;
}

.container {
    height: 100vh;
    display: flex;
    justify-content: space-between;
    /* border: 2px solid brown; */
}

.scroller {
    max-height: 100vh;
    overflow: scroll;
    scroll-snap-type: y mandatory;
    margin: 0 auto;
    width: 100%;
    position: absolute;
    scroll-behavior: smooth;
    z-index: 1;
 
}

.book {
    /* border: 20px solid; */
    margin: 0 auto;
    /* padding: 1em; */
    width: 300px;
    scroll-snap-align: center;
    scroll-padding-top: 300px;
    display: flex;
    flex-flow: column;
    justify-content: center;
    text-align: center;
    font-size: 4em;
}


.links {
    display: flex;
    justify-content: space-between;
    flex-direction: column;
    /* padding: 0 1em; */
}

.links h2 {
    font-family: 'Fredoka One';
    font-weight: 100;
    padding: 0;
}

.issue-list {
    width: fit-content;
    margin-left: auto;
    font-family: 'Arial';
    font-size: 18px;
    z-index: 1;
}

.issue-list p{
    margin: 0;
}

.issue-list p a {
    cursor: pointer;
    text-decoration: none;
    color: black;
}

.info {
    position: absolute;
    bottom: 0;
    /* padding: 0 1em; */
}

.info p {
    font-size: 19px;
    font-weight: bold;
    font-family: 'Chivo Mono';
}
<div id="background" class="container">
    <header>
        <h2>BACKSTAGE TALKS</h2>
    </header>
    <div class="info">
        <p> Backstage Talks is a magazine <br>
            casual, but in depth dialogues <br>
            on design and business. Our decisions <br>
            shape and influence this complex <br>
            world—to have a chance to make the <br>
            right ones, we need to talk </p>
    </div>
    <div id="scroll-box" class="scroller">
        <div class="book" id="book_issue-1">1</div>
        <div class="book" id="book_issue-2">2</div>
        <div class="book" id="book_issue-3">3</div>
        <div class="book" id="book_issue-4">4</div>
        <div class="book" id="book_issue-5">5</div>
        <div class="book" id="book_issue-6">6</div>
    </div>

    <div class="links">
        <h2>info@backstagetalks.com</h2>
        <div class="issue-list">
            <p id="issue1"><a href="#book_issue-1">Issue #1</a></p>
            <p id="issue2"><a href="#book_issue-2">Issue #2</a></p>
            <p id="issue3"><a href="#book_issue-3">Issue #3</a></p>
            <p id="issue4"><a href="#book_issue-4">Issue #4</a></p>
            <p id="issue5"><a href="#book_issue-5">Issue #5</a></p>
            <p id="issue6"><a href="#book_issue-6">Issue #6</a></p>
        </div>
    </div>


</div>
MaxiGui
  • 6,190
  • 4
  • 16
  • 33
TeDi
  • 11
  • 4
  • The value from `randomColor` can be invalid (0 - FF and 10000 - FFFFF), you can fix this by filling them up with leading zeroes. For example: `('00000' + Math.floor(Math.random() * 16777216).toString(16)).substr(-6)`. – Christopher Dec 21 '22 at 13:48

3 Answers3

1

You can check when one of your books div has snapped using a javascript function (scrollHandler in this example) like the one from this answer:

https://stackoverflow.com/a/66029649/5334486

Once you know when the "snapped" event happens, you can loop over your books divs and check which one of them is currently displayed testing whether getBoundingClientRect().top equals zero.

Finally, once you know which div is currently snapped, you can assign the same background color to body.

const gra = function(min, max) {
  return Math.random() * (max - min) + min;
};

const init = function() {
  let items = document.querySelectorAll(".scroller div");
  for (let i = 0; i < items.length; i++) {

    items[i].style.minHeight = gra(100, 100) + "vh";

    const randomColor = Math.floor(Math.random() * 16777215).toString(16);
    items[i].style.backgroundColor = "#" + randomColor;
    let itemColor = items[i].style.backgroundColor;
    // items[i].style.borderColor = "#" + ((1 << 24) * Math.random() | 0).toString(16);
  }
}
init();


function scrollHandler(e) {
  var atSnappingPoint = e.target.scrollTop % e.target.offsetHeight === 0;
  var timeOut = atSnappingPoint ? 0 : 150;

  clearTimeout(e.target.scrollTimeout);

  e.target.scrollTimeout = setTimeout(function() {
    //using the timeOut to evaluate scrolling state
    if (!timeOut) {
      console.log('Scroller snapped!');
      document.querySelectorAll(".scroller div").forEach(theDiv => {
         if (theDiv.id, theDiv.getBoundingClientRect().top == 0) {
             // THIS IS THE CURRENTLY SNAPPED BOOK
             console.log(theDiv.id, theDiv.style.backgroundColor)
             document.querySelector('body').style.backgroundColor = theDiv.style.backgroundColor
         }
      });
    }
  }, timeOut);
}



document.querySelector(".scroller").addEventListener('scroll', scrollHandler);
@import url('https://fonts.googleapis.com/css2?family=Bangers&family=Chivo+Mono:ital,wght@1,500&family=Hanalei+Fill&family=Fredoka+One&display=swap');
html,
body {
  margin: 0;
  /* font-family: Arial, Helvetica, sans-serif; */
}

::-webkit-scrollbar {
  display: none;
}

header {
  padding: 0.7em 0 0 0;
  color: red;
  font-family: 'Bangers';
  font-size: 25px;
}

header h2 {
  margin: 0;
}

.container {
  height: 100vh;
  display: flex;
  justify-content: space-between;
  /* border: 2px solid brown; */
}

.scroller {
  max-height: 100vh;
  overflow: scroll;
  scroll-snap-type: y mandatory;
  margin: 0 auto;
  width: 100%;
  position: absolute;
  scroll-behavior: smooth;
  z-index: 1;
}

.book {
  /* border: 20px solid; */
  margin: 0 auto;
  /* padding: 1em; */
  width: 300px;
  scroll-snap-align: center;
  scroll-padding-top: 300px;
  display: flex;
  flex-flow: column;
  justify-content: center;
  text-align: center;
  font-size: 4em;
}

.links {
  display: flex;
  justify-content: space-between;
  flex-direction: column;
  /* padding: 0 1em; */
}

.links h2 {
  font-family: 'Fredoka One';
  font-weight: 100;
  padding: 0;
}

.issue-list {
  width: fit-content;
  margin-left: auto;
  font-family: 'Arial';
  font-size: 18px;
  z-index: 1;
}

.issue-list p {
  margin: 0;
}

.issue-list p a {
  cursor: pointer;
  text-decoration: none;
  color: black;
}

.info {
  position: absolute;
  bottom: 0;
  /* padding: 0 1em; */
}

.info p {
  font-size: 19px;
  font-weight: bold;
  font-family: 'Chivo Mono';
}
<div id="background" class="container">
  <header>
    <h2>BACKSTAGE TALKS</h2>
  </header>
  <div class="info">
    <p> Backstage Talks is a magazine <br> casual, but in depth dialogues <br> on design and business. Our decisions <br> shape and influence this complex <br> world—to have a chance to make the <br> right ones, we need to talk </p>
  </div>
  <div id="scroll-box" class="scroller">
    <div class="book" id="book_issue-1">1</div>
    <div class="book" id="book_issue-2">2</div>
    <div class="book" id="book_issue-3">3</div>
    <div class="book" id="book_issue-4">4</div>
    <div class="book" id="book_issue-5">5</div>
    <div class="book" id="book_issue-6">6</div>
  </div>

  <div class="links">
    <h2>info@backstagetalks.com</h2>
    <div class="issue-list">
      <p id="issue1"><a href="#book_issue-1">Issue #1</a></p>
      <p id="issue2"><a href="#book_issue-2">Issue #2</a></p>
      <p id="issue3"><a href="#book_issue-3">Issue #3</a></p>
      <p id="issue4"><a href="#book_issue-4">Issue #4</a></p>
      <p id="issue5"><a href="#book_issue-5">Issue #5</a></p>
      <p id="issue6"><a href="#book_issue-6">Issue #6</a></p>
    </div>
  </div>


</div>
GrafiCode
  • 3,307
  • 3
  • 26
  • 31
  • Oh, thanks. This one worked just as perfect. I actually saw that solution in the link you referenced. But I'm curious as to why use 150 in the conditional statement? – TeDi Dec 22 '22 at 23:54
  • @TeDi it basically waits 150 milliseconds before evaluating the scrolling status, UNLESS the div is finished snapping (`e.target.scrollTop % e.target.offsetHeight === 0` means it is at the top of the viewport) in which case it doesn't wait and actually fires the routine. – GrafiCode Dec 23 '22 at 09:53
0

You can check through the scroll positions of the divs individually and attach a srcoll event listener on the .scroll-box, then assign the colors based on the scroll position.

const gra = function (min, max) {
    return Math.random() * (max - min) + min;
};

const init = function () {
    let items = document.querySelectorAll(".scroller div");
    let positions = [];
    let itemcolors = [];
    for (let i = 0; i < items.length; i++) {
        let item = items[i];
        
        item.style.minHeight = gra(100, 100) + "vh";
        const randomColor = Math.floor(Math.random() * 16777215).toString(16);
        item.style.backgroundColor = "#" + randomColor;
        let itemColor = item.style.backgroundColor;
        
        // Arrays to hold colors and position in same index positions
        itemcolors.push(itemColor)
        positions.push(item.offsetTop)
        /* on scrolling, get the offset and check against the existing
        scroll positions of the divs */
        
        document.getElementById('scroll-box').addEventListener('scroll', function(){
            let scrolloffset = document.getElementById('scroll-box').scrollTop
            positions.forEach((position, index) => {
                // Give an allowance of + or - 5 for padding and other spacing factors
                if(position >= (scrolloffset-5) && position <= (scrolloffset+5)){
                    document.getElementById('background').style.backgroundColor = itemcolors[index];
                }
            })
        })
        
        // items[i].style.borderColor = "#" + ((1 << 24) * Math.random() | 0).toString(16);
    }
    
}
init();
@import url('https://fonts.googleapis.com/css2?family=Bangers&family=Chivo+Mono:ital,wght@1,500&family=Hanalei+Fill&family=Fredoka+One&display=swap');

html, body{
    margin: 0;
    /* font-family: Arial, Helvetica, sans-serif; */
}
::-webkit-scrollbar {
    display: none;
}

header {
    padding: 0.7em 0 0 0 ;
    color: red;
    font-family: 'Bangers';
    font-size: 25px;
}

header h2 {
    margin: 0;
}

.container {
    height: 100vh;
    display: flex;
    justify-content: space-between;
    /* border: 2px solid brown; */
}

.scroller {
    max-height: 100vh;
    overflow: scroll;
    scroll-snap-type: y mandatory;
    margin: 0 auto;
    width: 100%;
    position: absolute;
    scroll-behavior: smooth;
    z-index: 1;
 
}

.book {
    /* border: 20px solid; */
    margin: 0 auto;
    /* padding: 1em; */
    width: 300px;
    scroll-snap-align: center;
    scroll-padding-top: 300px;
    display: flex;
    flex-flow: column;
    justify-content: center;
    text-align: center;
    font-size: 4em;
}


.links {
    display: flex;
    justify-content: space-between;
    flex-direction: column;
    /* padding: 0 1em; */
}

.links h2 {
    font-family: 'Fredoka One';
    font-weight: 100;
    padding: 0;
}

.issue-list {
    width: fit-content;
    margin-left: auto;
    font-family: 'Arial';
    font-size: 18px;
    z-index: 1;
}

.issue-list p{
    margin: 0;
}

.issue-list p a {
    cursor: pointer;
    text-decoration: none;
    color: black;
}

.info {
    position: absolute;
    bottom: 0;
    /* padding: 0 1em; */
}

.info p {
    font-size: 19px;
    font-weight: bold;
    font-family: 'Chivo Mono';
}
<div id="background" class="container">
    <header>
        <h2>BACKSTAGE TALKS</h2>
    </header>
    <div class="info">
        <p> Backstage Talks is a magazine <br>
            casual, but in depth dialogues <br>
            on design and business. Our decisions <br>
            shape and influence this complex <br>
            world—to have a chance to make the <br>
            right ones, we need to talk </p>
    </div>
    <div id="scroll-box" class="scroller">
        <div class="book" id="book_issue-1">1</div>
        <div class="book" id="book_issue-2">2</div>
        <div class="book" id="book_issue-3">3</div>
        <div class="book" id="book_issue-4">4</div>
        <div class="book" id="book_issue-5">5</div>
        <div class="book" id="book_issue-6">6</div>
    </div>

    <div class="links">
        <h2>info@backstagetalks.com</h2>
        <div class="issue-list">
            <p id="issue1"><a href="#book_issue-1">Issue #1</a></p>
            <p id="issue2"><a href="#book_issue-2">Issue #2</a></p>
            <p id="issue3"><a href="#book_issue-3">Issue #3</a></p>
            <p id="issue4"><a href="#book_issue-4">Issue #4</a></p>
            <p id="issue5"><a href="#book_issue-5">Issue #5</a></p>
            <p id="issue6"><a href="#book_issue-6">Issue #6</a></p>
        </div>
    </div>


</div>
Cypherjac
  • 859
  • 8
  • 17
  • Thank you! It worked when I tried this. But since this is the first time it's working as expected, I noticed that it loads to random divs instead of the first one. I tried use the scrollIntoView() function to get it to load the first div by default. It worked. But I still see that scrolling effect, which I don't want. Any ideas? – TeDi Dec 22 '22 at 13:39
  • If you've been able to fix the random divs issue.. Then to remove the scrolling effect you'll have to access the `scroll-behavior` of the **.scroller** class, since that's what is making it smooth scroll. You'll have to do this with javascript by setting it to `auto` a few moments before the page loads thereafter set it to `smooth`, – Cypherjac Dec 22 '22 at 14:14
  • Okay I did that. But it's still not giving me exactly what I wanted. Is it possible to make it always load the first div by default ? – TeDi Dec 22 '22 at 23:48
  • I'm not sure I get you well, by first div do you mean load the div with `id=book-issue-1` or do you mean the background should always have the color of the first div's background? If it's the latter, then I think it's already working that way since it always shows the **number 1** everytime it loads, so i don't really get your question. Please elaborate more – Cypherjac Dec 23 '22 at 00:16
  • Oh Okay. I meant the former. I want the first div to load by default without using scrollIntoView() function to always take it back there. Basically, I want to get rid of the random divs loading at page refresh, but I'm having trouble doing it. I don't know if it's a problem with the JS or CSS, because I've worked on a previous scroll-snap project and didn't have this issue. – TeDi Dec 23 '22 at 01:04
  • I think I've understood you now, the reason it's loading different divs for you on page refresh is because you're probably reloading the page when it has a value like `#book_issue-3` appended to the **URL** (After clicking one of those Issue links). So the page automatically scrolls to the div with that id, hence the scrolling effect to the div. But generally on a normal case without a **href** appended to the url it should load from the top by default. Remove that href from the url and see what happens. Meanwhile, I can implement a silent scroll feature since the issue is now clear – Cypherjac Dec 23 '22 at 07:06
0

You can test if an element is (completly) inside the viewport by using Element.getBoundingClientRect() and compare the values to the window w/h.

const gra = function(min, max) {
  return Math.random() * (max - min) + min;
};

const init = function() {
  let items = document.querySelectorAll(".scroller div");
  for (let i = 0; i < items.length; i++) {

    items[i].style.minHeight = gra(100, 100) + "vh";

    const randomColor = ('00000' + Math.floor(Math.random() * 16777216).toString(16)).substr(-6);
    items[i].style.backgroundColor = "#" + randomColor;
    let itemColor = items[i].style.backgroundColor;
    // items[i].style.borderColor = "#" + ((1 << 24) * Math.random() | 0).toString(16);
  }
}
init();

function isInViewport(element) {
  const { top, left, bottom, right } = element.getBoundingClientRect();
  return (
    top >= 0 &&
    left >= 0 &&
    bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
    right <= (window.innerWidth || document.documentElement.clientWidth)
  );
}
const scroller = document.querySelector(".scroller");

scroller.addEventListener("scroll", ({ target }) => {
  [...target.childNodes]
    .filter(child => child.nodeType != 3 && isInViewport(child))
    .forEach(child => background.style.backgroundColor = child.style.backgroundColor);
});
/* initial */
scroller.dispatchEvent(new Event("scroll"));
@import url('https://fonts.googleapis.com/css2?family=Bangers&family=Chivo+Mono:ital,wght@1,500&family=Hanalei+Fill&family=Fredoka+One&display=swap');
html,
body {
  margin: 0;
  /* font-family: Arial, Helvetica, sans-serif; */
}

::-webkit-scrollbar {
  display: none;
}

header {
  padding: 0.7em 0 0 0;
  color: red;
  font-family: 'Bangers';
  font-size: 25px;
}

header h2 {
  margin: 0;
}

.container {
  height: 100vh;
  display: flex;
  justify-content: space-between;
  /* border: 2px solid brown; */
}

.scroller {
  max-height: 100vh;
  overflow: scroll;
  scroll-snap-type: y mandatory;
  margin: 0 auto;
  width: 100%;
  position: absolute;
  scroll-behavior: smooth;
  z-index: 1;
}

.book {
  /* border: 20px solid; */
  margin: 0 auto;
  /* padding: 1em; */
  width: 300px;
  scroll-snap-align: center;
  scroll-padding-top: 300px;
  display: flex;
  flex-flow: column;
  justify-content: center;
  text-align: center;
  font-size: 4em;
}

.links {
  display: flex;
  justify-content: space-between;
  flex-direction: column;
  /* padding: 0 1em; */
}

.links h2 {
  font-family: 'Fredoka One';
  font-weight: 100;
  padding: 0;
}

.issue-list {
  width: fit-content;
  margin-left: auto;
  font-family: 'Arial';
  font-size: 18px;
  z-index: 1;
}

.issue-list p {
  margin: 0;
}

.issue-list p a {
  cursor: pointer;
  text-decoration: none;
  color: black;
}

.info {
  position: absolute;
  bottom: 0;
  /* padding: 0 1em; */
}

.info p {
  font-size: 19px;
  font-weight: bold;
  font-family: 'Chivo Mono';
}
<div id="background" class="container">
  <header>
    <h2>BACKSTAGE TALKS</h2>
  </header>
  <div class="info">
    <p> Backstage Talks is a magazine <br> casual, but in depth dialogues <br> on design and business. Our decisions <br> shape and influence this complex <br> world—to have a chance to make the <br> right ones, we need to talk </p>
  </div>
  <div id="scroll-box" class="scroller">
    <div class="book" id="book_issue-1">1</div>
    <div class="book" id="book_issue-2">2</div>
    <div class="book" id="book_issue-3">3</div>
    <div class="book" id="book_issue-4">4</div>
    <div class="book" id="book_issue-5">5</div>
    <div class="book" id="book_issue-6">6</div>
  </div>

  <div class="links">
    <h2>info@backstagetalks.com</h2>
    <div class="issue-list">
      <p id="issue1"><a href="#book_issue-1">Issue #1</a></p>
      <p id="issue2"><a href="#book_issue-2">Issue #2</a></p>
      <p id="issue3"><a href="#book_issue-3">Issue #3</a></p>
      <p id="issue4"><a href="#book_issue-4">Issue #4</a></p>
      <p id="issue5"><a href="#book_issue-5">Issue #5</a></p>
      <p id="issue6"><a href="#book_issue-6">Issue #6</a></p>
    </div>
  </div>


</div>

There is also a more modern way with the Intersection Observer API.

const gra = function(min, max) {
  return Math.random() * (max - min) + min;
};

const init = function() {
  let items = document.querySelectorAll(".scroller div");
  for (let i = 0; i < items.length; i++) {

    items[i].style.minHeight = gra(100, 100) + "vh";

    const randomColor = ('00000' + Math.floor(Math.random() * 16777216).toString(16)).substr(-6);
    items[i].style.backgroundColor = "#" + randomColor;
    let itemColor = items[i].style.backgroundColor;
    // items[i].style.borderColor = "#" + ((1 << 24) * Math.random() | 0).toString(16);
  }
}
init();

const options = {
  root: null,
  rootMargin: "0px",
  threshold: [0.51] // 51% visible
};

const observer = new IntersectionObserver((list) => {
  list.forEach(entry => {
      if (entry.intersectionRatio >= 0.51) {
        background.style.backgroundColor = entry.target.style.backgroundColor;
      }
  })
}, options);
/* observe every child except text nodes */
document.querySelector(".scroller").childNodes.forEach(child => child.nodeType != 3 && observer.observe(child));
@import url('https://fonts.googleapis.com/css2?family=Bangers&family=Chivo+Mono:ital,wght@1,500&family=Hanalei+Fill&family=Fredoka+One&display=swap');
html,
body {
  margin: 0;
  /* font-family: Arial, Helvetica, sans-serif; */
}

::-webkit-scrollbar {
  display: none;
}

header {
  padding: 0.7em 0 0 0;
  color: red;
  font-family: 'Bangers';
  font-size: 25px;
}

header h2 {
  margin: 0;
}

.container {
  height: 100vh;
  display: flex;
  justify-content: space-between;
  /* border: 2px solid brown; */
}

.scroller {
  max-height: 100vh;
  overflow: scroll;
  scroll-snap-type: y mandatory;
  margin: 0 auto;
  width: 100%;
  position: absolute;
  scroll-behavior: smooth;
  z-index: 1;
}

.book {
  /* border: 20px solid; */
  margin: 0 auto;
  /* padding: 1em; */
  width: 300px;
  scroll-snap-align: center;
  scroll-padding-top: 300px;
  display: flex;
  flex-flow: column;
  justify-content: center;
  text-align: center;
  font-size: 4em;
}

.links {
  display: flex;
  justify-content: space-between;
  flex-direction: column;
  /* padding: 0 1em; */
}

.links h2 {
  font-family: 'Fredoka One';
  font-weight: 100;
  padding: 0;
}

.issue-list {
  width: fit-content;
  margin-left: auto;
  font-family: 'Arial';
  font-size: 18px;
  z-index: 1;
}

.issue-list p {
  margin: 0;
}

.issue-list p a {
  cursor: pointer;
  text-decoration: none;
  color: black;
}

.info {
  position: absolute;
  bottom: 0;
  /* padding: 0 1em; */
}

.info p {
  font-size: 19px;
  font-weight: bold;
  font-family: 'Chivo Mono';
}
<div id="background" class="container">
  <header>
    <h2>BACKSTAGE TALKS</h2>
  </header>
  <div class="info">
    <p> Backstage Talks is a magazine <br> casual, but in depth dialogues <br> on design and business. Our decisions <br> shape and influence this complex <br> world—to have a chance to make the <br> right ones, we need to talk </p>
  </div>
  <div id="scroll-box" class="scroller">
    <div class="book" id="book_issue-1">1</div>
    <div class="book" id="book_issue-2">2</div>
    <div class="book" id="book_issue-3">3</div>
    <div class="book" id="book_issue-4">4</div>
    <div class="book" id="book_issue-5">5</div>
    <div class="book" id="book_issue-6">6</div>
  </div>

  <div class="links">
    <h2>info@backstagetalks.com</h2>
    <div class="issue-list">
      <p id="issue1"><a href="#book_issue-1">Issue #1</a></p>
      <p id="issue2"><a href="#book_issue-2">Issue #2</a></p>
      <p id="issue3"><a href="#book_issue-3">Issue #3</a></p>
      <p id="issue4"><a href="#book_issue-4">Issue #4</a></p>
      <p id="issue5"><a href="#book_issue-5">Issue #5</a></p>
      <p id="issue6"><a href="#book_issue-6">Issue #6</a></p>
    </div>
  </div>


</div>
Christopher
  • 3,124
  • 2
  • 12
  • 29