Opening divs within an if statement, closing them outside

Twine Version: 2.10.0

I would like to conditionally open and close divs from within if statements within a for loop. Here’s a simplification of the code I’m using:

<<for _a to 0; _a lt 4; _a++>>

<<if _a == 0>>
<div class="col1">
<</if>>
<<if _a == 1>>
<div class="col2">
<</if>>
<<if _a == 2>>
<div class="col3">
<</if>>
<<if _a == 3>>
<div class="col4">
<</if>>

You are player _a
</div>
<</for>>

This code prints the closing div tags as text and then says it can’t find the closing tags for the divs. Is there any way I can fix this problem?

You could try something like this:

<<for _a to 0; _a lt 4; _a++>>
    
    <<if _a == 0>>
        <<set _class = "col1">>
    <<elseif _a == 1>>
        <<set _class = "col2">>
    <<elseif _a == 2>>
        <<set _class = "col3">>
    <<elseif _a == 3>>
        <<set _class = "col4">>
    <</if>>
    
    <div @class=_class>You are player _a</div>
<</for>>

Instead of opening the <div>, it sets a variable with the desired class, then uses the evaluation directive to add the class to the <div>.

In this specific case, it might also be easier to do something like this:

<<for _a to 0; _a lt 4; _a++>>
    <<set _class = "col"+String(_a+1)>>
    <div @class=_class>You are player _a</div>
<</for>>
1 Like

Thank you! That worked great, but I have a follow-up question.

I’d like to present the results in a tables, with two results in each row, like this:

<table>
	<tr>
		<td>You are player 1</td>
		<td>You are player 2</td>
	</tr>
	<tr>
		<td>You are player 3</td>
		<td>You are player 4</td>
	</tr>
</table>	

I can’t see a way to achieve this using your technique. Is there a way to adapt it to give this result?

Try this:

<<set _table = "<table>">>
<<set _column = 0>>

<<for _a to 0; _a lt 4; _a++>>
    <<set _class = "col"+(_a+1)>>
    
    <<if _column == 0>>
        <<set _table += "<tr>">>
    <</if>>
    
    <<set _table += "<td><div class='"+_class+"'>You are player "+_a+"</div></td>">>
    
    <<if _column == 1>>
        <<set _table += "</tr>">>
    <</if>>
    
    <<set _column = (_column+1)%2>>
<</for>>

<<set _table += "</table>">>

_table

So, basically, it’s impossible to have only a </div> in a conditionally called statement, right? Because the SugarCube2 engine parses the code and doesn’t recognize that the </div> is set correctly?

I have this issue:
I want to have six buttons placed like this:
[1] [2] [3]
[4] [5] [6]
They will have content and mouse-over events dependent on the loop variable, so they should be best placed in a <<for>> loop.
The two rows are rendered by a respective <div class="row"> element.
So the output code should look like this:

<div class="row"> /* first row with [1] [2] [3] */
  <<for _i to 0; _i lt 6; _i++>>
  <div class="button">{button-rendering code}</div>
  <<if _i eq 2>> /* after the first 3 buttons, switch the row */
</div> /* end of first row with [1] [2] [3] */
<div class="row"> /* second row with [4] [5] [6] */
  <</if>>
  <</for>>
</div> /* end of second row with [4] [5] [6] */

When I code it like this, SugarCube says that it can’t find the closing </div> element and refuses to render the sequence.
I can circumvent the problem with two loops, one with _i={0,1,2} and one with _i={3,4,5}. But this is extremely ugly :wink:
Is there no other way to have a simple, singular loop?

Thanks, Pesha.

You could do something like in the code above, where instead of immediately printing the html elements you add them to a string then print that:

<<set _buttons to '<div class="row">'>>
<<for _i to 0; _i lt 6; _i++>>
  <<set _buttons += '<div class="button">{button-rendering code}</div>'>>
  <<if _i eq 2>>
    <<set _buttons += '</div><div class="row">'>>
  <</if>>
<</for>>
<<set _buttons += '</div>'>>

_buttons

I “solved” it like this atm:

<div class="row">

<<set $miniGroupBtnId to 1>><<include MiniGroupBtn>>
<<set $miniGroupBtnId to 2>><<include MiniGroupBtn>>
<<set $miniGroupBtnId to 3>><<include MiniGroupBtn>>

</div>
<br>
<div class="row">

<<set $miniGroupBtnId to 4>><<include MiniGroupBtn>>
<<set $miniGroupBtnId to 5>><<include MiniGroupBtn>>
<<set $miniGroupBtnId to 6>><<include MiniGroupBtn>>

</div>

This is also not the “prettiest” solution but the rendering code in passage MiniGroupBtn is 29 lines long and does “advanced” things like <<capture>>, calculations of the parameters to the passage that will be called when one of the buttons is activated and so on. So I’m not sure if the approach with the print buffer would work. So I won’t check it out now. But thank you for the suggestion. I’m sure it will come handy in the future.

Thanks, Pesha.

You may want to take a look at CSS grid layouts, which will let you arrange things in rows without extra containing elements.

e.g.
css

.grid {
   display: grid;
   grid-template-columns: 1fr 1fr 1fr;
}

passage

<div class="grid">
<<set $miniGroupBtnId to 1>><<include MiniGroupBtn>>
<<set $miniGroupBtnId to 2>><<include MiniGroupBtn>>
<<set $miniGroupBtnId to 3>><<include MiniGroupBtn>>
<<set $miniGroupBtnId to 4>><<include MiniGroupBtn>>
<<set $miniGroupBtnId to 5>><<include MiniGroupBtn>>
<<set $miniGroupBtnId to 6>><<include MiniGroupBtn>>
</div>

You may also want to look at <<widget>> in the SugarCube documentation, as a way to replace your use of include with something more self-contained, so you can do:

<div class="grid">
<<MiniGroupBtn 1>>
<<MiniGroupBtn 2>>
<<MiniGroupBtn 3>>
<<MiniGroupBtn 4>>
<<MiniGroupBtn 5>>
<<MiniGroupBtn 6>>
</div>

Thank you very much, David.
Your idea with the grid layout and the <<widget>> element helped a lot. It’s a beautiful solution.
Though I found that not only is a .grid class needed but also each element has to be inside its own class.
For all interested, this is my solution (omitting a lot of actual display code, just the absolute necessary stuff).

  1. Definitions of css elements:
.grid {
   display: grid;
   height: 50px;
   width: 40px;
   gap: 2px;
   grid-template-columns: 1fr 1fr 1fr;
}
.grid-item {
  text-align: center;
  background-color: lime;
}
  1. Definition of the MiniGroupBtn <<widget>>. According to the SugarCube documentation, it has to be defined inside a passage tagged with a tag ‘widget’. I created a passage called “Widgets” with an appropriate tag and this content:
<<nobr>>
<<widget "MiniGroupBtn">>
<<if _args[0] gte 1 and _args[0] lte 6>>
  <div class="grid-item">_args[0]</div>
<</if>>
<</widget>>
<</nobr>>

I found the <<nobr>> tag has to be placed outside the <<widget>> definition for best results. Ymmv.
3. Then inside the appropriate passage, I call it like this:

<div class="grid">
<<MiniGroupBtn 1>>
<<MiniGroupBtn 2>>
<<MiniGroupBtn 3>>
<<MiniGroupBtn 4>>
<<MiniGroupBtn 5>>
<<MiniGroupBtn 6>>
</div>

It’s not showing here, but the whole passage was put inside a <<nobr>> to avoid “strange” behavior.

This generates something like this:
(unfortunately I apparently can’t post pictures)

[1][2][3]
[4][5][6]

Thanks
Pesha.

Of course, the “real” call to the <<widget>> for rendering is more like this:

<div class="grid">
<<for _i to 1; _i lte 6 ; _i++>>
  <<MiniGroupBtn _i>>
<</for>>
</div>

Which lets you see the true beauty of David’s solution :slight_smile: