---
title: "Create a story map, fly to a location based on the scroll position | JavaScript maps SDK"
description: "Fly to locations on the map by scrolling."
---

> For AI agents: a documentation index is available at [/llms.txt](https://docs.maptiler.com/llms.txt). Markdown versions of all pages are available by appending .md to any URL.

# Create a story map, fly to a location based on the scroll position

Create a story map. As you scroll down the page, the map will automatically move to the specific location of each chapter in the story.

### NPM module setup

```bash
npm install --save @maptiler/sdk
```

#### main.js

```js
import { Map, MapStyle, config } from '@maptiler/sdk';
import '@maptiler/sdk/dist/maptiler-sdk.css';

config.apiKey = 'YOUR_MAPTILER_API_KEY_HERE';
const map = new Map({
    container: 'map',
    style: MapStyle.STREETS, 
    center: [-0.15591514, 51.51830379],
    zoom: 15.5,
    bearing: 27,
    pitch: 45
});

const chapters = {
    'baker': {
        bearing: 27,
        center: [-0.15591514, 51.51830379],
        zoom: 15.5,
        pitch: 20
    },
    'aldgate': {
        duration: 6000,
        center: [-0.07571203, 51.51424049],
        bearing: 150,
        zoom: 15,
        pitch: 0
    },
    'london-bridge': {
        bearing: 90,
        center: [-0.08533793, 51.50438536],
        zoom: 13,
        speed: 0.6,
        pitch: 40
    },
    'woolwich': {
        bearing: 90,
        center: [0.05991101, 51.48752939],
        zoom: 12.3
    },
    'gloucester': {
        bearing: 45,
        center: [-0.18335806, 51.49439521],
        zoom: 15.3,
        pitch: 20,
        speed: 0.5
    },
    'caulfield-gardens': {
        bearing: 180,
        center: [-0.19684993, 51.5033856],
        zoom: 12.3
    },
    'telegraph': {
        bearing: 90,
        center: [-0.10669358, 51.51433123],
        zoom: 17.3,
        pitch: 40
    },
    'charing-cross': {
        bearing: 90,
        center: [-0.12416858, 51.50779757],
        zoom: 14.3,
        pitch: 20
    }
};

// On every scroll event, check which element is on screen
window.onscroll = function () {
    const chapterNames = Object.keys(chapters);
    for (let i = 0; i < chapterNames.length; i++) {
        const chapterName = chapterNames[i];
        if (isElementOnScreen(chapterName)) {
            setActiveChapter(chapterName);
            break;
        }
    }
};

let activeChapterName = 'baker';
function setActiveChapter(chapterName) {
    if (chapterName === activeChapterName) return;

    map.flyTo(chapters[chapterName]);

    document.getElementById(chapterName).setAttribute('class', 'active');
    document.getElementById(activeChapterName).setAttribute('class', '');

    activeChapterName = chapterName;
}

function isElementOnScreen(id) {
    const element = document.getElementById(id);
    const bounds = element.getBoundingClientRect();
    return bounds.top < window.innerHeight && bounds.bottom > 0;
}
```

#### index.html

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="robots" content="noindex">
  <title>Fly to a location based on scroll position</title>
  <link rel="stylesheet" href="./style.css">
</head>
<body>
  <style>
  #map {
      position: fixed;
      width: 50%;
  }
  #features {
      width: 50%;
      margin-left: 50%;
      font-family: sans-serif;
      overflow-y: scroll;
      background-color: #fafafa;
  }
  section {
      padding: 25px 50px;
      line-height: 25px;
      border-bottom: 1px solid #ddd;
      opacity: 0.25;
      font-size: 13px;
  }
  section.active {
      opacity: 1;
  }
  section:last-child {
      border-bottom: none;
      margin-bottom: 200px;
  }
  </style>

  <div id="map"></div>
  <div id="features">
  <section id="baker" class="active">
      <h3>221b Baker St.</h3>
      <p>
          November 1895. London is shrouded in fog and Sherlock Holmes and
          Watson pass time restlessly awaiting a new case. "The London
          criminal is certainly a dull fellow," Sherlock bemoans. "There have
          been numerous petty thefts," Watson offers in response. Just then a
          telegram arrives from Sherlock's brother Mycroft with a mysterious
          case.
      </p>
  </section>
  <section id="aldgate">
      <h3>Aldgate Station</h3>
      <p>
          Arthur Cadogan West was found dead, head crushed in on train tracks
          at Aldgate Station at 6AM Tuesday morning. West worked at Woolwich
          Arsenal on the Bruce-Partington submarine, a secret military
          project. Plans for the submarine had been stolen and seven of the
          ten missing papers were found in West's possession. Mycroft implores
          Sherlock to take the case and recover the three missing papers.
      </p>
  </section>
  <section id="london-bridge">
      <h3>London Bridge</h3>
      <p>
          Holmes and Watson's investigations take them across London. Sherlock
          deduces that West was murdered elsewhere, then moved to Aldgate
          Station to create the illusion that he was crushed on the tracks by
          a train. On their way to Woolwich Sherlock dispatches a telegram to
          Mycroft at London Bridge: "Send list of all foreign spies known to
          be in England, with full address."
      </p>
  </section>
  <section id="woolwich">
      <h3>Woolwich Arsenal</h3>
      <p>
          While investigating at Woolwich Arsenal Sherlock learns that West
          did not have the three keys&mdash;door, office, and
          safe&mdash;necessary to steal the papers. The train station clerk
          mentions seeing an agitated West boarding the 8:15 train to London
          Bridge. Sherlock suspects West of following someone who had access
          to the Woolwich chief's keyring with all three keys.
      </p>
  </section>
  <section id="gloucester">
      <h3>Gloucester Road</h3>
      <p>
          Mycroft responds to Sherlock's telegram and mentions several spies.
          Hugo Oberstein of 13 Caulfield Gardens catches Sherlock's eye. He
          heads to the nearby Gloucester Road station to investigate and
          learns that the windows of Caulfield Gardens open over rail tracks
          where trains stop frequently.
      </p>
  </section>
  <section id="caulfield-gardens">
      <h3>13 Caulfield Gardens</h3>
      <p>
          Holmes deduces that the murderer placed West atop a stopped train at
          Caulfield Gardens. The train traveled to Aldgate Station before
          West's body finally toppled off. Backtracking to the criminal's
          apartment, Holmes finds a series of classified ads from
          <em>The Daily Telegraph</em> stashed away. All are under the name
          Pierrot: "Monday night after nine. Two taps. Only ourselves. Do not
          be so suspicious. Payment in hard cash when goods delivered."
      </p>
  </section>
  <section id="telegraph">
      <h3>The Daily Telegraph</h3>
      <p>
          Holmes and Watson head to The Daily Telegraph and place an ad to
          draw out the criminal. It reads: "To-night. Same hour. Same place.
          Two taps. Most vitally important. Your own safety at stake.
          Pierrot." The trap works and Holmes catches the criminal: Colonel
          Valentine Walter, the brother of Woolwich Arsenal's chief. He
          confesses to working for Hugo Oberstein to obtain the submarine
          plans in order to pay off his debts.
      </p>
  </section>
  <section id="charing-cross">
      <h3>Charing Cross Hotel</h3>
      <p>
          Walter writes to Oberstein and convinces him to meet in the smoking
          room of the Charing Cross Hotel where he promises additional plans
          for the submarine in exchange for money. The plan works and Holmes
          and Watson catch both criminals.
      </p>
      <small id="citation">
          Adapted from
          <a href="https://www.gutenberg.org/files/2346/2346-h/2346-h.htm"
              >Project Gutenberg</a
          >
      </small>
  </section>
  </div>
  <script type="module" src="./main.js"></script>
</body>
</html>
```

#### style.css

```css
body {margin: 0; padding: 0;}
#map {position: absolute; top: 0; bottom: 0; width: 100%;}
```

### Basic JavaScript setup

```html
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Fly to a location based on scroll position</title>
  <script src="https://cdn.maptiler.com/maptiler-sdk-js/v4.2.0/maptiler-sdk.umd.min.js"></script>
  <link href="https://cdn.maptiler.com/maptiler-sdk-js/v4.2.0/maptiler-sdk.css" rel="stylesheet" />
  <style>
    body {
      margin: 0;
      padding: 0;
    }

    #map {
      position: fixed;
      width: 50%;
      top: 0;
      bottom: 0;
    }

    #features {
      width: 50%;
      margin-left: 50%;
      font-family: sans-serif;
      overflow-y: scroll;
      background-color: #fafafa;
    }

    section {
      padding: 25px 50px;
      line-height: 25px;
      border-bottom: 1px solid #ddd;
      opacity: 0.25;
      font-size: 13px;
    }

    section.active {
      opacity: 1;
    }

    section:last-child {
      border-bottom: none;
      margin-bottom: 200px;
    }
  </style>
</head>

<body>
  <div id="map"></div>
  <div id="features">
    <section id="baker" class="active">
      <h3>221b Baker St.</h3>
      <p>
        November 1895. London is shrouded in fog and Sherlock Holmes and
        Watson pass time restlessly awaiting a new case. "The London
        criminal is certainly a dull fellow," Sherlock bemoans. "There have
        been numerous petty thefts," Watson offers in response. Just then a
        telegram arrives from Sherlock's brother Mycroft with a mysterious
        case.
      </p>
    </section>
    <section id="aldgate">
      <h3>Aldgate Station</h3>
      <p>
        Arthur Cadogan West was found dead, head crushed in on train tracks
        at Aldgate Station at 6AM Tuesday morning. West worked at Woolwich
        Arsenal on the Bruce-Partington submarine, a secret military
        project. Plans for the submarine had been stolen and seven of the
        ten missing papers were found in West's possession. Mycroft implores
        Sherlock to take the case and recover the three missing papers.
      </p>
    </section>
    <section id="london-bridge">
      <h3>London Bridge</h3>
      <p>
        Holmes and Watson's investigations take them across London. Sherlock
        deduces that West was murdered elsewhere, then moved to Aldgate
        Station to create the illusion that he was crushed on the tracks by
        a train. On their way to Woolwich Sherlock dispatches a telegram to
        Mycroft at London Bridge: "Send list of all foreign spies known to
        be in England, with full address."
      </p>
    </section>
    <section id="woolwich">
      <h3>Woolwich Arsenal</h3>
      <p>
        While investigating at Woolwich Arsenal Sherlock learns that West
        did not have the three keys&mdash;door, office, and
        safe&mdash;necessary to steal the papers. The train station clerk
        mentions seeing an agitated West boarding the 8:15 train to London
        Bridge. Sherlock suspects West of following someone who had access
        to the Woolwich chief's keyring with all three keys.
      </p>
    </section>
    <section id="gloucester">
      <h3>Gloucester Road</h3>
      <p>
        Mycroft responds to Sherlock's telegram and mentions several spies.
        Hugo Oberstein of 13 Caulfield Gardens catches Sherlock's eye. He
        heads to the nearby Gloucester Road station to investigate and
        learns that the windows of Caulfield Gardens open over rail tracks
        where trains stop frequently.
      </p>
    </section>
    <section id="caulfield-gardens">
      <h3>13 Caulfield Gardens</h3>
      <p>
        Holmes deduces that the murderer placed West atop a stopped train at
        Caulfield Gardens. The train traveled to Aldgate Station before
        West's body finally toppled off. Backtracking to the criminal's
        apartment, Holmes finds a series of classified ads from
        <em>The Daily Telegraph</em> stashed away. All are under the name
        Pierrot: "Monday night after nine. Two taps. Only ourselves. Do not
        be so suspicious. Payment in hard cash when goods delivered."
      </p>
    </section>
    <section id="telegraph">
      <h3>The Daily Telegraph</h3>
      <p>
        Holmes and Watson head to The Daily Telegraph and place an ad to
        draw out the criminal. It reads: "To-night. Same hour. Same place.
        Two taps. Most vitally important. Your own safety at stake.
        Pierrot." The trap works and Holmes catches the criminal: Colonel
        Valentine Walter, the brother of Woolwich Arsenal's chief. He
        confesses to working for Hugo Oberstein to obtain the submarine
        plans in order to pay off his debts.
      </p>
    </section>
    <section id="charing-cross">
      <h3>Charing Cross Hotel</h3>
      <p>
        Walter writes to Oberstein and convinces him to meet in the smoking
        room of the Charing Cross Hotel where he promises additional plans
        for the submarine in exchange for money. The plan works and Holmes
        and Watson catch both criminals.
      </p>
      <small id="citation">
        Adapted from
        <a href="https://www.gutenberg.org/files/2346/2346-h/2346-h.htm">Project Gutenberg</a>
      </small>
    </section>
  </div>
  <script>
    maptilersdk.config.apiKey = 'YOUR_MAPTILER_API_KEY_HERE';
    const map = new maptilersdk.Map({
      container: 'map',
      style: maptilersdk.MapStyle.STREETS,
      center: [-0.15591514, 51.51830379],
      zoom: 15.5,
      bearing: 27,
      pitch: 45
    });

    const chapters = {
      'baker': {
        bearing: 27,
        center: [-0.15591514, 51.51830379],
        zoom: 15.5,
        pitch: 20
      },
      'aldgate': {
        duration: 6000,
        center: [-0.07571203, 51.51424049],
        bearing: 150,
        zoom: 15,
        pitch: 0
      },
      'london-bridge': {
        bearing: 90,
        center: [-0.08533793, 51.50438536],
        zoom: 13,
        speed: 0.6,
        pitch: 40
      },
      'woolwich': {
        bearing: 90,
        center: [0.05991101, 51.48752939],
        zoom: 12.3
      },
      'gloucester': {
        bearing: 45,
        center: [-0.18335806, 51.49439521],
        zoom: 15.3,
        pitch: 20,
        speed: 0.5
      },
      'caulfield-gardens': {
        bearing: 180,
        center: [-0.19684993, 51.5033856],
        zoom: 12.3
      },
      'telegraph': {
        bearing: 90,
        center: [-0.10669358, 51.51433123],
        zoom: 17.3,
        pitch: 40
      },
      'charing-cross': {
        bearing: 90,
        center: [-0.12416858, 51.50779757],
        zoom: 14.3,
        pitch: 20
      }
    };

    // On every scroll event, check which element is on screen
    window.onscroll = function () {
      const chapterNames = Object.keys(chapters);
      for (let i = 0; i < chapterNames.length; i++) {
        const chapterName = chapterNames[i];
        if (isElementOnScreen(chapterName)) {
          setActiveChapter(chapterName);
          break;
        }
      }
    };

    let activeChapterName = 'baker';
    function setActiveChapter(chapterName) {
      if (chapterName === activeChapterName) return;

      map.flyTo(chapters[chapterName]);

      document.getElementById(chapterName).setAttribute('class', 'active');
      document.getElementById(activeChapterName).setAttribute('class', '');

      activeChapterName = chapterName;
    }

    function isElementOnScreen(id) {
      const element = document.getElementById(id);
      const bounds = element.getBoundingClientRect();
      return bounds.top < window.innerHeight && bounds.bottom > 0;
    }
  </script>
</body>

</html>
```

## Related examples

### [Fit to the bounds of a lineString](https://docs.maptiler.com/sdk-js/examples/zoomto-linestring/)

Examples

Get the bounds of a lineString.

### [Fit a map to a bounding box](https://docs.maptiler.com/sdk-js/examples/fitbounds/)

Examples

Fit map to a specific area using fitBounds method.

### [Fly to a location](https://docs.maptiler.com/sdk-js/examples/flyto/)

Examples

Smoothly interpolate between locations using the flyTo animation.

### [Creates an animated map to fly between different locations](https://docs.maptiler.com/sdk-js/examples/heatmap-animated/)

Examples

Animate map flights between locations using a heatmap layer.
