Scrolling to new text on click instead of to top of page

Hi all,

I recently found a post here, “Automatically scrolling to new text” which addresses a major issue I’m having with a story I’m working on (Twine 2, Harlowe 3), but the solution is giving me an error message.

I’m not sure if this should be a reply to that post so my apologies if so! It’s from 2020, so I didn’t know if anyone would see a reply. I am also new here :slight_smile:

I want the story I’m working on to function similarly to the one in that post, except all of my new paragraphs within a passage appear on a click anywhere on the page with (click: ?page)[== instead of a link. Thus, when I have many paragraphs in one passage, the clicks to add new text send the reader to the top of the page and they have to scroll back down to read what was added. It makes for an awkward reading experience.

Based on that old post, I’m trying to use scrollIntoView on each paragraph so that the page focuses on the new paragraph on click instead of scrolling to the top of the passage.

I tested the OP’s solution by just putting it into my Twine exactly as is to see if it works for me. And it does seem to work, as in the scrolling is working in test mode, but I get the following error every time when I test the passage:

There is a problem with this story’s 1st script:
TypeError: Cannot read properties of null (reading ‘scrollIntoView’)
TypeError: Cannot read properties of null

Does anybody know what I’m missing? I have only added the code to the passage and created the empty footer passage, nothing on the CSS or JavaScript, though I tried variations of that.

I think I’m probably missing something basic to make .scrollIntoView work, but I don’t know what. I’ve only been writing IF and playing around with Twine for about two months and there are definitely a lot of things I haven’t figured out yet!

Any ideas greatly appreciated!

For context to @tmerk’s quesion…
Automatically scrolling to new text

1 Like

Without posting your code, I can’t discern why you are getting an error.

However, here is a more deliberate and fool-proof method.

Scrolling the bottom of the page into view only works well if your new content is shorter than the visible area. If the content is taller, then you’ll have to scroll up to start reading the new content. This is not desirable.

Here is an example of code that scrolls the new element into view. If the element is taller than the viewable space, it will stop at the top of the new content (where it starts) and the user can scroll down to read the rest.

(link: "Part 1")[(show: ?part1)<script>$('tw-hook[name="part1"]')[0].scrollIntoView({ behavior: "smooth", block: "start" })</script>]

|part1)[
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer egestas iaculis nulla, a convallis diam hendrerit ut. Nam ut lorem aliquam, auctor est a, aliquam eros. Donec sed vestibulum tortor. Mauris consectetur, metus ac placerat efficitur, arcu leo faucibus lacus, in volutpat lorem massa a dolor. Ut interdum id ipsum eu sagittis. Ut elit sem, pretium vitae dignissim a, luctus eget odio. Phasellus et metus sit amet risus vulputate tempor. In congue nunc orci, ut bibendum est iaculis vitae.

(link: "Part 2")[(show: ?part2)<script>$('tw-hook[name="part2"]')[0].scrollIntoView({ behavior: "smooth", block: "start" })</script>]
]

|part2)[
Aenean sit amet ipsum vel nisl semper cursus. Donec eu augue rutrum, luctus lacus et, accumsan urna. Maecenas ac rhoncus nisi, in vehicula leo. Donec eu efficitur ligula. Sed enim enim, elementum nec sodales sit amet, malesuada non justo. Suspendisse feugiat, tellus nec ullamcorper commodo, ipsum nunc luctus risus, sed mattis ipsum lectus at nulla. Vestibulum non sodales felis. Fusce venenatis molestie est eu pulvinar. Phasellus a dapibus arcu. Mauris non fringilla lectus. Suspendisse dignissim, lacus non lobortis tincidunt, ex quam efficitur est, sed commodo neque felis a libero. 

(link: "Part 3")[(show: ?part3)<script>$('tw-hook[name="part3"]')[0].scrollIntoView({ behavior: "smooth", block: "start" })</script>]
]

|part3)[
Nulla a luctus sapien, ut lacinia magna. Donec scelerisque lobortis diam, sed ornare turpis posuere ac. Curabitur sagittis volutpat egestas. Etiam luctus odio eget leo ullamcorper aliquam. Etiam id accumsan nisi, eget volutpat magna. Aliquam auctor porta nibh, vitae faucibus augue tempus ut. Nullam in accumsan eros. Aliquam hendrerit dictum libero, in elementum quam malesuada sit amet. Aliquam pharetra massa at erat pulvinar, ac imperdiet nisi rutrum. Etiam quis risus sapien. Etiam nunc orci, venenatis vel quam at, maximus ornare urna. 

(link: "Part 4")[(show: ?part4)<script>$('tw-hook[name="part4"]')[0].scrollIntoView({ behavior: "smooth", block: "start" })</script>]
]

|part4)[
Mauris pulvinar magna quis dolor aliquet aliquam. Vivamus fringilla lobortis hendrerit. Proin blandit imperdiet ipsum pellentesque imperdiet. In pretium sem vel feugiat fringilla. Sed ac fringilla erat, vitae dapibus erat. Integer eget elit ac arcu tristique accumsan ac ut turpis. Cras euismod molestie leo, et facilisis magna tempor sit amet. Duis eleifend rhoncus enim at tempus. 

**That's it!**]

I only briefly tested this, but it seems to work as expected.

Let us know if this helps.

Thanks for the reply! This seems to work well, and I am not getting the error when I test it.

The only thing I haven’t been able to figure out is how to get the first link to be a (click: ?page) instead of a link. I replaced the other links (for parts 2 - 4) with a page click without a problem, but for some reason, it doesn’t work for the first one. Here is the code that works with clicks, leaving the first one a link:

(link: "Part 1")[(show: ?part1)<script>$('tw-hook[name="part1"]')[0].scrollIntoView({ behavior: "smooth", block: "start" })</script>]

|part1)[
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer egestas iaculis nulla, a convallis diam hendrerit ut. Nam ut lorem aliquam, auctor est a, aliquam eros. Donec sed vestibulum tortor. Mauris consectetur, metus ac placerat efficitur, arcu leo faucibus lacus, in volutpat lorem massa a dolor. Ut interdum id ipsum eu sagittis. Ut elit sem, pretium vitae dignissim a, luctus eget odio. Phasellus et metus sit amet risus vulputate tempor. In congue nunc orci, ut bibendum est iaculis vitae.

(click: ?page)[==[(show: ?part2)<script>$('tw-hook[name="part2"]')[0].scrollIntoView({ behavior: "smooth", block: "start" })</script>]
]

|part2)[
Aenean sit amet ipsum vel nisl semper cursus. Donec eu augue rutrum, luctus lacus et, accumsan urna. Maecenas ac rhoncus nisi, in vehicula leo. Donec eu efficitur ligula. Sed enim enim, elementum nec sodales sit amet, malesuada non justo. Suspendisse feugiat, tellus nec ullamcorper commodo, ipsum nunc luctus risus, sed mattis ipsum lectus at nulla. Vestibulum non sodales felis. Fusce venenatis molestie est eu pulvinar. Phasellus a dapibus arcu. Mauris non fringilla lectus. Suspendisse dignissim, lacus non lobortis tincidunt, ex quam efficitur est, sed commodo neque felis a libero. 

(click: ?page)[==[(show: ?part3)<script>$('tw-hook[name="part3"]')[0].scrollIntoView({ behavior: "smooth", block: "start" })</script>]
]

|part3)[
Nulla a luctus sapien, ut lacinia magna. Donec scelerisque lobortis diam, sed ornare turpis posuere ac. Curabitur sagittis volutpat egestas. Etiam luctus odio eget leo ullamcorper aliquam. Etiam id accumsan nisi, eget volutpat magna. Aliquam auctor porta nibh, vitae faucibus augue tempus ut. Nullam in accumsan eros. Aliquam hendrerit dictum libero, in elementum quam malesuada sit amet. Aliquam pharetra massa at erat pulvinar, ac imperdiet nisi rutrum. Etiam quis risus sapien. Etiam nunc orci, venenatis vel quam at, maximus ornare urna. 

(click: ?page)[==[(show: ?part4)<script>$('tw-hook[name="part4"]')[0].scrollIntoView({ behavior: "smooth", block: "start" })</script>]
]

|part4)[
Mauris pulvinar magna quis dolor aliquet aliquam. Vivamus fringilla lobortis hendrerit. Proin blandit imperdiet ipsum pellentesque imperdiet. In pretium sem vel feugiat fringilla. Sed ac fringilla erat, vitae dapibus erat. Integer eget elit ac arcu tristique accumsan ac ut turpis. Cras euismod molestie leo, et facilisis magna tempor sit amet. Duis eleifend rhoncus enim at tempus. 

**That's it!**]

And this is the code that doesn’t work:

(click: ?page)[==[(show: ?part1)<script>$('tw-hook[name="part1"]')[0].scrollIntoView({ behavior: "smooth", block: "start" })</script>]

|part1)[
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer egestas iaculis nulla, a convallis diam hendrerit ut. Nam ut lorem aliquam, auctor est a, aliquam eros. Donec sed vestibulum tortor. Mauris consectetur, metus ac placerat efficitur, arcu leo faucibus lacus, in volutpat lorem massa a dolor. Ut interdum id ipsum eu sagittis. Ut elit sem, pretium vitae dignissim a, luctus eget odio. Phasellus et metus sit amet risus vulputate tempor. In congue nunc orci, ut bibendum est iaculis vitae.

(click: ?page)[==[(show: ?part2)<script>$('tw-hook[name="part2"]')[0].scrollIntoView({ behavior: "smooth", block: "start" })</script>]
]

|part2)[
Aenean sit amet ipsum vel nisl semper cursus. Donec eu augue rutrum, luctus lacus et, accumsan urna. Maecenas ac rhoncus nisi, in vehicula leo. Donec eu efficitur ligula. Sed enim enim, elementum nec sodales sit amet, malesuada non justo. Suspendisse feugiat, tellus nec ullamcorper commodo, ipsum nunc luctus risus, sed mattis ipsum lectus at nulla. Vestibulum non sodales felis. Fusce venenatis molestie est eu pulvinar. Phasellus a dapibus arcu. Mauris non fringilla lectus. Suspendisse dignissim, lacus non lobortis tincidunt, ex quam efficitur est, sed commodo neque felis a libero. 

(click: ?page)[==[(show: ?part3)<script>$('tw-hook[name="part3"]')[0].scrollIntoView({ behavior: "smooth", block: "start" })</script>]
]

|part3)[
Nulla a luctus sapien, ut lacinia magna. Donec scelerisque lobortis diam, sed ornare turpis posuere ac. Curabitur sagittis volutpat egestas. Etiam luctus odio eget leo ullamcorper aliquam. Etiam id accumsan nisi, eget volutpat magna. Aliquam auctor porta nibh, vitae faucibus augue tempus ut. Nullam in accumsan eros. Aliquam hendrerit dictum libero, in elementum quam malesuada sit amet. Aliquam pharetra massa at erat pulvinar, ac imperdiet nisi rutrum. Etiam quis risus sapien. Etiam nunc orci, venenatis vel quam at, maximus ornare urna. 

(click: ?page)[==[(show: ?part4)<script>$('tw-hook[name="part4"]')[0].scrollIntoView({ behavior: "smooth", block: "start" })</script>]
]

|part4)[
Mauris pulvinar magna quis dolor aliquet aliquam. Vivamus fringilla lobortis hendrerit. Proin blandit imperdiet ipsum pellentesque imperdiet. In pretium sem vel feugiat fringilla. Sed ac fringilla erat, vitae dapibus erat. Integer eget elit ac arcu tristique accumsan ac ut turpis. Cras euismod molestie leo, et facilisis magna tempor sit amet. Duis eleifend rhoncus enim at tempus. 

**That's it!**]

Any thoughts on why I can’t swap out the first link, or if/how I could fix it? Thanks again!

1 Like

You edited the code by using an unclosed hook → [==

This feature of Harlowe stops the rendering of all content until the click is executed. This means that when you click the first page link…

  • it then executes the (show: ?part1) macro, but the |part1) hook doesn’t exist yet
  • it then runs the <script> tag that scrolls to the part1 hook, but it doesn’t exist yet
  • then it renders a hidden hook called part1 and sits waiting for the content to be revealed

So don’t use the unclosed hook [== code and just use closed [ ] brackets. That way the entire page content is rendered, but the hooks remain hidden and available to the (show:) macro and the <script> tag code.

(click: ?page)[(show: ?part1)<script>$('tw-hook[name="part1"]')[0].scrollIntoView({ behavior: "smooth", block: "start" })</script>]

I hope that makes sense.

1 Like

Ah I see, that is one of the nuances I hadn’t learned yet, but it makes sense. Everything is working as I want it to now. Thank you so much!

1 Like