Grabbing data from a dynamic navigation website with Puppeteer

I previously posted my issue in a separate thread without receiving any responses or suggestions. Consequently, I am presenting it in a different format to seek assistance.

The primary objective of my script is to obtain a list of matches from a specific webpage. To validate the script’s functionality, I initially conducted a manual test using the Chrome Developer Console:

Chrome Console test Results

In my attempt to achieve this objective using Puppeteer, I formulated the following script:

const puppeteer = require('puppeteer')
const fs = require('fs/promises')



async function start()
{
    const browser = await puppeteer.launch({headless: false, ignoreHTTPSErrors: true}) // Open browser in non-headless mode for visibility

const page = await browser.newPage()
await page.goto("https://tickets.fcbayern.com/internetverkauf/EventList.aspx")



const names = await page.evaluate(() => {
    return Array.from(document.querySelectorAll('.fcb-gr-8 .fcb-row strong')).map(x => x.textContent.trim())
})
await fs.writeFile("names.txt", names.join("rn"))


}

start()

Ther error which I got was:

throw new Error(‘Execution context was destroyed, most likely because of a navigation.’);

While executing the code, I observed that the last URL accessed just before encountering the error was:
“https://tickets.fcbayern.com/internetverkauf/start.aspx?error=login_required”

URL fired just before Exception

Hence, I conjectured that the issue might stem from a lack of authentication. This assumption led me to initiate a previous thread seeking guidance on how to address login-related challenges.

How to get past the login page and handle Redirections with puppeteer?

If anyone could offer advice or assistance in ensuring the correctness of my approach, I would greatly appreciate it.

What’s the best way to store page links in next.js?

Is there any way to optimize the storage of links to different pages so that I don’t have to write the same links on each page?

I am also interested in your practices on this issue. Do you copy links conditionally on every page or do you use some other methods?

I thought about storing them in an .env file, but that doesn’t seem to work with client components. I also created a file with changes where there was an array with all the pages, but this is also a bad option, because through the code you can get links to private pages that are intended only for the administration.

How can I overflow item from a grid to another grid in the same hierachy?

I have a container div, inside with a header and body div (both are grid display) to recreate a table with scrollable and sticky header, following this guide. But I have an additional requirement which is to collapse the header and span it across the body to be something like this.
expected table output.
So my plan is to have a positioned absolute item inside the grid and set the width to span across the body also.

Here is my code HTML

<div class="wrap">
  <div class="headers">
    <div class="scroller syncscroll" name="myElements">
      <div class="track">
        <div class="heading">Heading 1</div>
      </div>
      <div class="track">
        <div class="heading">Heading 2</div>
      </div>
      <div class="track relative">

        <div class="collapsible">Heading 7</div>
      </div>

    </div>
  </div>
  <div class="tracks syncscroll" name="myElements">
    <div class="track">
      <div class="entry">
        <h3>Lorem, ipsum dolor.</h3>
      </div>
      <div class="entry">
        <h3>Lorem, ipsum.</h3>
      </div>
      <div class="entry">
        <h3>Lorem, ipsum.</h3>
      </div>
    </div>
    <div class="track">
      <div class="entry">
        <h3>Lorem, ipsum dolor.</h3>
      </div>
      <div class="entry">
        <h3>Lorem, ipsum.</h3>
      </div>
      <div class="entry">
        <h3>Lorem, ipsum.</h3>
      </div>
    </div>
    <div class="track">
      <div class="entry">
        <h3>Lorem, ipsum dolor.</h3>
      </div>
      <div class="entry">
        <h3>Lorem, ipsum.</h3>
      </div>
      <div class="entry">
        <h3>Lorem, ipsum.</h3>
      </div>
    </div>
  </div>
</div>

CSS

body {
  background: #f5f7fa;
  overflow-x: hidden;
}

.wrap {
  position: relative;
  margin: 10em auto 30em;
  max-width: 500px;
  overscroll-behavior: contain;
}

.headers {
  top: 0;
  position: -webkit-sticky;
  position: sticky;
  z-index: 1;
}

.tracks,
.scroller {
  display: grid;
  grid-template-columns: repeat(7, 200px);
  
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
}

.scroller {
  overflow-x: hidden;
}

.tracks {
  overflow: auto;
  scroll-snap-type: x mandatory;
}

.scenes::-webkit-scrollbar,
.scroller::-webkit-scrollbar {
  display: none;
}

.track + .track {
  margin-left: -1px;
}

.relative {
  position: relative;
}

.collapsible {
  top: 0px;
  position: absolute;
  left: 0px;
  width: 100%;
  height: 200px !important;

  background-color: aqua;
  line-height: 30px;
  // max-width: 30px;
  color: white;
  z-index: 9;
  cursor: pointer;
  writing-mode: vertical-lr;
  text-align: center;
}

.heading {
  height: 50px;
  display: flex;
  justify-content: center;
  align-items: center;
  position: -webkit-sticky;
  position: sticky;
  top: 0;
  border: solid #fff;
  border-width: 0 1px;
  z-index: 1;
  background: #efefef;
  font-weight: 700;
}

.entry {
  border: 1px solid #ebebeb;
  border-top: 0;
  background: #fff;
  height: 8em;
  padding: 1em;
}
}

This code pen shows my problem. For some reason, it seems like the collapsible bar (in blue) cannot span outside of the header grid component. Is this a limitation of grid layout or am I missing something? so far, I only find questions to avoid overflowing the grid only

If it is a limitation, is there any other way to achieve something similar.

Get console logs in Chrome with Selenium 4 and Javascript

I am using selenium for a super simple test, using chrome; I am trying to get in the chrome tools some console.logs for debugging, but for some reasons logs are not available at all. I also tried console.warn and console.error to check if it was a matter of debug levels, but still nothing.

I used an argument to open chrome tools at start: options.addArguments('--auto-open-devtools-for-tabs') (you can see it below);

Here’s my code:

import { Builder, By, Key } from 'selenium-webdriver'
import { assert, expect, should as loadShould } from 'chai'
import { it } from 'mocha'

import chrome from 'selenium-webdriver/chrome.js'

// patch to make should chainable
const should = loadShould()

let driver = {}

describe('a second test block', async () => {

  before(async () => {

    let options = new chrome.Options()
    options.addArguments('--window-size=1980,1024')
    options.addArguments('--auto-open-devtools-for-tabs')
    
    options.SetLoggingPreference(LogType.Browser, LogLevel.All)    // <--- here I'm trying to enable logging

    var capabilities = {
      'browserstack.timezone': 'Rome',
      // ... other capabilities
    }

    driver = await new Builder()
      .withCapabilities(capabilities)
      .forBrowser('chrome')
      .setChromeOptions(options)
      .build()
  })

  it('another test', async () => {

    // trying to log, but nothing shows up
    console.log('start')
    
    // navigate to app
    await driver.get('https://lambdatest.github.io/sample-todo-app/')

    // add a todo
    await driver.findElement(By.id('sampletodotext')).sendKeys('learn selenium', Key.RETURN)

    // assert
    let todoText = await driver.findElement(By.xpath('//li[last()]')).getText()

    assert.strictEqual(todoText, 'learn selenium')

    expect(todoText).that.is.not.a('number')

    todoText.should.equal('learn selenium')

    // close the browser
    // await driver.quit()    // <--- commented out to check chrome tools console
  })
})

This code throws an error about the arguments in options.SetLoggingPreference; if I remove this line, instead of printing in chrome tools console start, it shows nothing.

I read about this question in which the answer is made using Python, but I need to do it in Javascript.

I also tried reading all the chrome capabilities and the chrome command line arguments (which can be added with options.addArguments(), but nothing worked.

any clue on how to do it?

How to make the number of the order sequential and with a different prefix for every language, using Polylang

Good morning everyone and happy new year. I need some advice on how to proceed with the following implementation. For my site in multiple languages ​​that uses Polylang as a plugin, I would need to have separate and consecutive numbering for the orders. For example: Italian: IT-2024-01, IT-2024-02, IT-2024-03 German: DE-2024-01, DE-2024-02, DE-2024-03. Thank you for your interesting insights.
Right now I just used this snippet for the italian:

/**

  • Orders Prefix
    */

add_filter( ‘woocommerce_order_number’, ‘change_woocommerce_order_number’ );

function change_woocommerce_order_number( $order_id ) {
$order = wc_get_order( $order_id );

if ( $order && $order->get_date_created() ) {
    $order_year = $order->get_date_created()->format( 'Y' );
    $prefix = 'IT-' . $order_year . '-';
    $new_order_id = $prefix . $order_id;
    return $new_order_id;
}

return $order_id;

}

Thanks, from my heart!

React Native: Issue with Navigation – Screen Transition Not Working (New Problem)

I’m currently working on a React Native project and facing a unique issue with screen navigation. Despite setting up the navigation stack correctly, the screen transition is not working as expected. Here’s a simplified version of my code:

// App.js
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import HomeScreen from './screens/HomeScreen';
import DetailsScreen from './screens/DetailsScreen';

const Stack = createStackNavigator();

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName="Home">
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Details" component={DetailsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}
// HomeScreen.js
import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { useNavigation } from '@react-navigation/native';

export default function HomeScreen() {
  const navigation = useNavigation();

  const handleNavigate = () => {
    navigation.navigate('Details');
  };

  return (
    <View>
      <Text>Welcome to the Home Screen!</Text>
      <TouchableOpacity onPress={handleNavigate}>
        <Text>Go to Details Screen</Text>
      </TouchableOpacity>
    </View>
  );
}

Despite a seemingly correct setup, pressing “Go to Details Screen” doesn’t navigate to the DetailsScreen. No error messages are displayed, and I’ve verified that the screen names match.

Can someone please provide guidance on how to troubleshoot and resolve this new issue with the screen transition not working as expected in React Native navigation? Any insights or suggestions would be appreciated. Thank you!

How do I update and insert recent scores of a typing test into a text file using VSC?

In our typathon app, we want to store the WPM and accuracy of most recent 10 games of that user (according to their login username) into their own txt file. However with the coding logic now, the program duplicates the WPM and accuracy into 2 index of the txt file. Example:

(username), (password), (average WPM), (average accuracy), (best WPM), (best accuracy), (most recent game WPM), (most recent accuracy), …… until 9 more game scores are stored.

Right now, the program stores the most recent scores into 4 slots instead of the first 2.

The code in one of the controllers:

private static String insertResults(String line, String username, double wpm, double acc) {
        int[] parts = convertToIntegerArray(line.split(","));
        int[] updatedParts = Arrays.copyOf(parts, parts.length);

        //parts[0] username, parts[1] password, parts[2] avg wpm, parts[3] avg acc, parts[4] best wpm, parts[5] best acc
        int avgWPM = parts[2]; int avgACC = parts[3];
        int totalWPM = 0, totalACC = 0;

        if(parts[24] != 0) { //If full, set the longest results to 0
                parts[24] = 0;
                parts[25] = 0;
            }

        for(int i = 24; i >= 8; i -= 2) { //Update 10 latest wpm and acc, odd is wpm, even is acc
            updatedParts[i] = parts[i - 2];
            updatedParts[i + 1] = parts[i - 1];
        }
        
        updatedParts[6] = (int) wpm; updatedParts[7] = (int) acc; //insert recent game wpm and acc

        for(int i = 6; i < updatedParts.length; i += 2) { //calculate average wpm and acc
            totalWPM += updatedParts[i];
            totalACC += updatedParts[i + 1];
        }

        int games = 0;
        for(int i = 6; i < updatedParts.length; i +=2) {
            if(updatedParts[i] != 0) {
                games++;
            }
        }

        avgWPM = (int) totalWPM / games ; avgACC = (int) totalACC / games ;
        updatedParts[2] = avgWPM; updatedParts[3] = avgACC;

        if(updatedParts[2] > updatedParts[4]) { //compare recent wpm and acc with best wpm and acc
            updatedParts[4] = updatedParts[2];
        }

        if(updatedParts[3] > updatedParts[5]) {
            updatedParts[5] = updatedParts[3];
        }

        return convertToString(updatedParts);
    }

This is the code I have now but I can’t figure out the error. This code updates a line with the updated scores and values and rewrites it back into the txt file

getting items from an embed array in discord js

So I got an interaction embed using this code :

client.on('interactionCreate', async (interaction) => {
    if (!interaction.isButton()) return;
    const raidembed = interaction.message.embeds
    console.log(raidembed)

And this is what it returns :

[
  Embed {
    data: {
      type: 'rich',
      title: 'RAID DU : A :',
      image: [Object],
      footer: [Object],
      fields: [Array],
      description: '# Caveau des Incarnationsn### <:skull:1193157255407882340> Normal (:)',
      color: 9124820
    }
  }
]

I need to edit the fields of the embed, and i am currently using the embed.spliceFields() method.
How do i get to the fields array of the embed array ?
I tried console.log(raidembed.fields) or console.log(raidembed.data.fields but they all return “undefined”.

It looks to me like an array inside an object, inside an other object and inside an other array :

[object { object: { array: []}}]

Thanks in advance for your help.

Webstorm gives error: Access to XMLHttpRequest at ‘http://localhost:1234/upload/’ from origin ‘http://localhost:63342’ has been blocked by CORS policy

bros, I launch JS project in WebStorm at http://localhost:1234 (npm run serve in the terminal) and separately run a debug there on the index.html page (via the debug configuration) at http://localhost:63342/project directory /src/index.html. At the second address I click a button, by clicking on which I select a file that should be loaded into a directory Upload at http://localhost:1234/upload, but I get an error “Access to XMLHttpRequest has been blocked by CORS policy”. I tried everything:

  1. Chrome plugins don’t help – I tried 3 different ones

  2. I’ve edited index.js at the path ../node_modules/cors, inserted for example at the beginning

const express = require('express');
const cors = require('cors');

const app = express();
app.use(cors());
app.options('*', cors());

and all sorts of other combinations, which i’ve found in the net – it doesn’t help

  1. Added in Webstorm IDE .htaccess support plugin and wrote this one line in the .htaccess file, which i put in the project’s directory:
Header set Access-Control-Allow-Origin '*'

Doesn’t help either. Please, help me overcome it, I’m already exhausted because nothing works out for me that worked out for others. Maybe I added strings in the wrong index.js at the second point? Or I made .htaccess file the wrong way at the third point? How can I check it out? Or maybe there is another way?

Tried all things that were advised on the net: 1) installing chrome plugins, 2) editing of the index.js file, 3) editing of the .htaccess file

Dark-Mode toggling Issue

Following is my DarkMode.jsx Component When I switch button ,dark mode apply only to navbar.
Other remaining Part of website not changed to Dark theme.How should I keep State updated everywhere.
using Material UI I am trying darkMode.

import React,{useState} from 'react';
import { ThemeProvider, createTheme } from "@mui/material/styles";
import CssBaseline from "@mui/material/CssBaseline";
import {  Switch,  } from "@mui/material"
export default function DarkMode() {

  // state to manage the dark mode
  const [toggleDarkMode, setToggleDarkMode] = useState(true);

  // function to toggle the dark mode as true or false
  const toggleDarkTheme = () => {
    setToggleDarkMode(!toggleDarkMode);
  };

  // applying the primary and secondary theme colors
  const darkTheme = createTheme({
    palette: {
      mode: toggleDarkMode ? 'dark' : 'light', // handle the dark mode state on toggle
      primary: {
        main: '#90caf9',
      },
      secondary: {
        main: '#131052',

      },
    },
  });

  return (
   <ThemeProvider theme={darkTheme}>
    <CssBaseline/>
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
        <h2>Toggle Dark mode</h2>
        <Switch checked={toggleDarkMode} onChange={toggleDarkTheme} />
    </div>
   </ThemeProvider>
  )
}

I add my DarkMode Component inside Main.jsx after Navbar component

import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
// import App from './App'
import Navbar from './Components/components/Navbar';
import Hero from './Components/components/Hero';
import About from './Components/components/About';
import Feature from './Components/components/Feature';
import Card from './Components/components/Card';
import Opensource from './Components/components/Opensource';
import Footer from './Components/components/Footer';
import Workshop from './Components/components/Workshop';
import DarkMode from './Components/components/DarkMode';



ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <Navbar/>
    <DarkMode/>
   <Hero />
   <Card />
   <About />
   <Feature />
   <Workshop/>
   <Opensource />
   <Footer />
  
  </React.StrictMode>,
)

Copy absolute URL including div id on button click

In my site, every h2 tag is containing copy to clipboard icon, so when I hit the copy icon button, the URL should copy including div id.

Example, the absolute URL is “mysite dot com” and the div id is “process-invoices” so when I click on the copy button then the url should copy like this “mysite.com/#process-invoices”

<div class="wrapper" id="process-invoices">
  <h1>Process invoices</h1>
  <button onclick="copy absolute url with above div id value">Copy to Clipboard</button>
</div>

JavaScript structuredClone got `Illegal invocation` in Chrome/Edge, but not in NodeJS

Run the following code in a browser:

({ clone: structuredClone }).clone(1);

will got Uncaught TypeError: Illegal invocation, tested in Chrome/Edge.

However running the code in NodeJS is fine, tested in NodeJS v20.

Workaround:

({ clone: (v) => structuredClone(v) }).clone(1);

Or

({ clone: function(v) { return structuredClone(v)} }).clone(1);

Is this the expected behaviour?

the last timeline item is cropped out

I have the code for a horizontal timeline with javascript. This code works fine, but there is one problem in scrolling. Sometimes when I get to the last timeline item, the scrolling is not complete and the NEXT button disappears.
can you please tell me how to fix this? thanks!

problem screenshot:

enter image description here

code:

(function () {
    // VARIABLES
    const timeline = document.querySelector(".timeline ol"),
        elH = document.querySelectorAll(".timeline li > div"),
        arrows = document.querySelectorAll(".timeline .arrows .arrow"),
        arrowPrev = document.querySelector(".timeline .arrows .arrow__prev"),
        arrowNext = document.querySelector(".timeline .arrows .arrow__next"),
        firstItem = document.querySelector(".timeline li:first-child"),
        lastItem = document.querySelector(".timeline li:last-child"),
        xScrolling = 280,
        disabledClass = "disabled";

    // START
    window.addEventListener("load", init);

    function init() {
        setEqualHeights(elH);
        animateTl(xScrolling, arrows, timeline);
    }

    // SET EQUAL HEIGHTS
    function setEqualHeights(el) {
        let counter = 0;
        for (let i = 0; i < el.length; i++) {
            const singleHeight = el[i].offsetHeight;

            if (counter < singleHeight) {
                counter = singleHeight;
            }
        }

        for (let i = 0; i < el.length; i++) {
            el[i].style.height = `${counter}px`;
        }
    }

    // CHECK IF AN ELEMENT IS IN VIEWPORT
    // http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
    function isElementInViewport(el) {
        const rect = el.getBoundingClientRect();
        return rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth);
    }

    // SET STATE OF PREV/NEXT ARROWS
    function setBtnState(el, flag = true) {
        if (flag) {
            el.classList.add(disabledClass);
        } else {
            if (el.classList.contains(disabledClass)) {
                el.classList.remove(disabledClass);
            }
            el.disabled = false;
        }
    }

    // ANIMATE TIMELINE
    function animateTl(scrolling, el, tl) {
        let counter = 0;
        for (let i = 0; i < el.length; i++) {
            el[i].addEventListener("click", function () {
                if (!arrowPrev.disabled) {
                    arrowPrev.disabled = true;
                }
                if (!arrowNext.disabled) {
                    arrowNext.disabled = true;
                }
                const sign = this.classList.contains("arrow__prev") ? "" : "-";
                if (counter === 0) {
                    tl.style.transform = `translateX(-${scrolling}px)`;
                } else {
                    const tlStyle = getComputedStyle(tl);
                    // add more browser prefixes if needed here
                    const tlTransform = tlStyle.getPropertyValue("-webkit-transform") || tlStyle.getPropertyValue("transform");
                    const values = parseInt(tlTransform.split(",")[4]) + parseInt(`${sign}${scrolling}`);
                    tl.style.transform = `translateX(${values}px)`;
                }

                setTimeout(() => {
                    isElementInViewport(firstItem) ? setBtnState(arrowPrev) : setBtnState(arrowPrev, false);
                    isElementInViewport(lastItem) ? setBtnState(arrowNext) : setBtnState(arrowNext, false);
                }, 1100);

                counter++;
            });
        }
    }
})();
.timeline {
    white-space: nowrap;
    overflow-x: hidden;
}

.timeline ol {
    font-size: 0;
    width: 100vw;
    padding: 250px 0;
    transition: all 1s;
}

.timeline ol li {
    position: relative;
    display: inline-block;
    list-style-type: none;
    width: 160px;
    height: 3px;
    background: #fff;
}

.timeline ol li:last-child {
    width: 280px;
}

.timeline ol li:not(:first-child) {
    margin-left: 14px;
}

.timeline ol li:not(:last-child)::after {
    content: "";
    position: absolute;
    top: 50%;
    left: calc(100% + 1px);
    bottom: 0;
    width: 12px;
    height: 12px;
    transform: translateY(-50%);
    border-radius: 50%;
    background: #f45b69;
}

.timeline ol li div {
    position: absolute;
    left: calc(100% + 7px);
    width: 280px;
    padding: 15px;
    font-size: 1rem;
    white-space: normal;
    color: black;
    background: white;
}

.timeline ol li div::before {
    content: "";
    position: absolute;
    top: 100%;
    left: 0;
    width: 0;
    height: 0;
    border-style: solid;
}

.timeline ol li:nth-child(odd) div {
    top: -16px;
    transform: translateY(-100%);
}

.timeline ol li:nth-child(odd) div::before {
    top: 100%;
    border-width: 8px 8px 0 0;
    border-color: white transparent transparent transparent;
}

.timeline ol li:nth-child(even) div {
    top: calc(100% + 16px);
}

.timeline ol li:nth-child(even) div::before {
    top: -8px;
    border-width: 8px 0 0 8px;
    border-color: transparent transparent transparent white;
}

.timeline time {
    display: block;
    font-size: 1.2rem;
    font-weight: bold;
    margin-bottom: 8px;
}

.timeline .arrows {
    display: flex;
    justify-content: center;
    margin-bottom: 20px;
}

.timeline .arrows .arrow__prev {
    margin-right: 20px;
}

.timeline .disabled {
    opacity: 0.5;
}

.timeline .arrows img {
    width: 45px;
    height: 45px;
}
<section class="timeline">
    <ol>
        <li>
            <div><time>1934</time> At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium</div>
        </li>
        <li>
            <div><time>1937</time> Proin quam velit, efficitur vel neque vitae, rhoncus commodo mi. Suspendisse finibus mauris et bibendum molestie. Aenean ex augue, varius et pulvinar in, pretium non nisi.</div>
        </li>
        <li>
            <div><time>1940</time> Proin iaculis, nibh eget efficitur varius, libero tellus porta dolor, at pulvinar tortor ex eget ligula. Integer eu dapibus arcu, sit amet sollicitudin eros.</div>
        </li>
        <li>
            <div><time>1943</time> In mattis elit vitae odio posuere, nec maximus massa varius. Suspendisse varius volutpat mattis. Vestibulum id magna est.</div>
        </li>
        <li>
            <div><time>1946</time> In mattis elit vitae odio posuere, nec maximus massa varius. Suspendisse varius volutpat mattis. Vestibulum id magna est.</div>
        </li>
        <li>
            <div><time>1956</time> In mattis elit vitae odio posuere, nec maximus massa varius. Suspendisse varius volutpat mattis. Vestibulum id magna est.</div>
        </li>
        <li>
            <div><time>1957</time> In mattis elit vitae odio posuere, nec maximus massa varius. Suspendisse varius volutpat mattis. Vestibulum id magna est.</div>
        </li>
        <li>
            <div><time>1967</time> Aenean condimentum odio a bibendum rhoncus. Ut mauris felis, volutpat eget porta faucibus, euismod quis ante.</div>
        </li>
        <li>
            <div><time>1977</time> Vestibulum porttitor lorem sed pharetra dignissim. Nulla maximus, dui a tristique iaculis, quam dolor convallis enim, non dignissim ligula ipsum a turpis.</div>
        </li>
        <li>
            <div><time>1985</time> In mattis elit vitae odio posuere, nec maximus massa varius. Suspendisse varius volutpat mattis. Vestibulum id magna est.</div>
        </li>
        <li>
            <div><time>2000</time> In mattis elit vitae odio posuere, nec maximus massa varius. Suspendisse varius volutpat mattis. Vestibulum id magna est.</div>
        </li>
        <li>
            <div><time>2005</time> In mattis elit vitae odio posuere, nec maximus massa varius. Suspendisse varius volutpat mattis. Vestibulum id magna est.</div>
        </li>
        <li></li>
    </ol>

    <div class="arrows">
        <button class="arrow arrow__prev disabled" disabled>
            <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/arrow_prev.svg" alt="prev timeline arrow" />
        </button>
        <button class="arrow arrow__next">
            <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/arrow_next.svg" alt="next timeline arrow" />
        </button>
    </div>
</section>