[Snowman] JS script doesn't run in page order

This is a simplified version of my problem. I have two passages, and the second is rendered within the first.

:: one
<div class="test">
This is passage one
</div>
<%= story.render( "two" ) %>

:: two
<script>
$('.test').addClass('red')
</script>

<div class="test">
This is passage two.
</div>

Add to this a CSS declaration .red {color:red}.

My intended result is:

<div class="red test">
This is passage one.
</div>
<div class="test">
This is passage two.
</div>

But the actual result is:

<div class="red test">
This is passage one.
</div>
<div class="red test">
This is passage two.
</div>

See what I’m trying to do? I’m expecting the script tag to be run in the place where it’s written, before the second “.test” div is added to the DOM, so it can only select the first “.test”. However, the script runs some time later, selecting both “.test” divs.

Is there anything I’m doing wrong? I need a systematic solution for this: I’m not doing this by hand in a few passages, it’s the way that the whole game works: rendering a passage within the current passage, then changing the previous text. (It’s of course a lot more complex than this example.)

Thanks!

The issue is that <script> elements are always executed after the contents of the ‘current’ Passage have been processed, so as current written the 2nd .test classed will always exist.

There are a number of methods you can use to achieve the result you want, like using a more precise CSS Selector

1: Use a pseudo class like :first-of-type to target the first instance of the .test class.

<script>
$('.test:first-of-type').addClass('red')
</script>

2: Combine :not( selector ) with :last-of-type to target every instance of the .test class except the last one.

<script>
$('.test:not(:last-of-type)').addClass('red')
</script>
1 Like
  1. I hate Javascript.

  2. Great idea @Greyelf! Thanks. I had to adapt it because my real divs aren’t siblings, but I could do it using .first() instead of :first-of-type.