停止 enumitem \item 跨页

停止 enumitem \item 跨页

一位同事问了这个问题。

你能自动要求枚举自定义列表中的项目不要跨分页符吗?假设这是针对考试的,考试中希望每个问题都一起显示,并且问题不超过一页。

原始上下文的简单版本如下。

\documentclass{article}
\usepackage[english]{babel} % needed by blindtext
\usepackage[pangram]{blindtext} % to produce dummy text only
\usepackage{enumitem}
\newlist{questions}{enumerate}{3}
\setlist[questions,1]{label*=\textbf{\arabic*}.}
\setlist[questions,2]{label=(\alph{questionsii})}
\setlist[questions,3]{label=(\roman{questionsiii})}

\begin{document}
    ~\vspace{15cm} % to push the text near the page break
    
    \begin{questions}
        \item \Blindtext[1][4]
        \item \Blindtext[2][4]
        \begin{questions}
            \item \Blindtext[1][4]
            \item \Blindtext[1][4]
            \item \Blindtext[1][4]
        \end{questions}
    \end{questions}
\end{document}

我有几个想法,我会在这里将它们作为答案发布,因为(a)他说他在这个问题上的搜索并没有让他找到任何可以工作的东西;(b)我对可能更好的其他解决方案感兴趣。

答案1

这在大多数情况下应该有效。我们\interlinepenalty=10000在环境中设置questions\nobreak在后面添加\par;由于\item发出惩罚,因此项目之间可以中断。

\documentclass{article}
\usepackage[english]{babel} % needed by blindtext
\usepackage[pangram]{blindtext} % to produce dummy text only
\usepackage{enumitem}
\newlist{questions}{enumerate}{3}
\setlist[questions,1]{label*=\textbf{\arabic*}.}
\setlist[questions,2]{label=(\alph{questionsii})}
\setlist[questions,3]{label=(\roman{questionsiii})}

\makeatletter
\AddToHook{env/questions/begin}{%
  \ifnum\enitdp@questions=0
    \interlinepenalty=10000
    \AddToHook{para/after}[nobreak]{\nobreak}%
  \fi
}
\AddToHook{env/questions/end}{%
  \ifnum\enitdp@questions=1
    \RemoveFromHook{para/after}[nobreak]%
  \fi
}
\makeatother

\begin{document}

\vspace*{15cm} % to push the text near the page break
    
    \begin{questions}
        \item \Blindtext[1][4]
        \item \Blindtext[2][4]
        \begin{questions}
            \item \Blindtext[1][4]
            \item \Blindtext[1][4]
            \item \Blindtext[1][4]
        \end{questions}
    \end{questions}

\end{document}

由于存在嵌套的可能性,questions我们只想将钩子添加到一次,因此我们在深度计数器为零时执行此操作(就像目前所看到的一样) 。仅当深度计数器为 1 时,即在外层,才会env/questions/begin删除。\nobreak\end{questions}

在此处输入图片描述

答案2

要使文本块不跨页,可以将其放在环境中\begin{minipage}...\end{minipage}。我能想到几种方法来实现这一点。所有这些方法都会产生相同的结果,不同之处在于实现方式。我想我更喜欢选项 2,它在前言中更复杂,但在我看来使用起来更简单。

1. 将每个项目放入其自己的小页面中。

基本上,您需要执行此操作,并且可以手动执行:

\begin{questions}
    \begin{minipage}{\linewidth}
        \item question text
    \end{minipage}
\end{questions}

这可能有点难记,所以你可以定义一个新命令来为你做这件事。这可以作为环境或命令来完成,但请注意,两者都会改变通常的使用习惯,\item text因为 minipage 有开始和结束,而\item只有开始。

1a. 作为环境。

我们nobreakitem在前言中定义了一个名为 的环境。此环境用于代替命令\item。前面的两个换行符\begin{minipage}是必需的,因为否则它会认为minipage是前一个的一部分\item,并且不会在它们之间放置换行符。它可能只用于有问题的\item,或用于所有\item。对我来说,这有点奇怪,因为你正在执行\begin\end而不是通常的操作\item,而且与自己编写上述代码相比,它并没有节省太多。

\documentclass{article}
\usepackage[english]{babel} % needed by blindtext
\usepackage[pangram]{blindtext} % to produce dummy text only
\usepackage{enumitem}
\newlist{questions}{enumerate}{3}
\setlist[questions,1]{label*=\textbf{\arabic*}.}
\setlist[questions,2]{label=(\alph{questionsii})}
\setlist[questions,3]{label=(\roman{questionsiii})}

\newenvironment{nobreakitem}{
    
\begin{minipage}{\linewidth}\item}{\end{minipage}}

\begin{document}
    ~\vspace{15cm} % to push the text near the page break
    
    \begin{questions}
        \begin{nobreakitem}
            \Blindtext[1][4]
        \end{nobreakitem}
        \begin{nobreakitem}
            \Blindtext[2][4]
            \begin{questions}
                \item \Blindtext[1][4]
                \item \Blindtext[1][4]
                \item \Blindtext[1][4]
            \end{questions}
        \end{nobreakitem}
    \end{questions}
\end{document}

1b. 作为命令。

我们在前言中定义了一个名为 的命令\nobreakitem。标准\item仅适用于其后的内容,但这里我们需要告诉它将 放在哪里\end{minipage},所以我们必须用 括住后面的内容{}并将其传递给我们的命令,这是对 的一个不幸的改变\item。同样,定义中的换行符\newcommand是必要的。

\documentclass{article}
\usepackage[english]{babel} % needed by blindtext
\usepackage[pangram]{blindtext} % to produce dummy text only
\usepackage{enumitem}
\newlist{questions}{enumerate}{3}
\setlist[questions,1]{label*=\textbf{\arabic*}.}
\setlist[questions,2]{label=(\alph{questionsii})}
\setlist[questions,3]{label=(\roman{questionsiii})}

\newcommand{\nobreakitem}[1]{
    
\begin{minipage}{\linewidth}\item #1\end{minipage}}

\begin{document}
    ~\vspace{15cm} % to push the text near the page break
    
    \begin{questions}
        \nobreakitem{\Blindtext[1][4]}
        \nobreakitem{\Blindtext[2][4]
        \begin{questions}
            \item \Blindtext[1][4]
            \item \Blindtext[1][4]
            \item \Blindtext[1][4]
        \end{questions}}
    \end{questions}
\end{document}

2. 保留通常使用方法的替代方案\item

\item为了在问题中保留通常使用的,我们可以使用 的功能enumitem,其中列表可以恢复上一个列表。这里每个都是包装在 中的\begin{question}实例,但编号为全局问题集的延续。每个应该只包含一个顶级,但它可以包含子部分(以通常的方式通过 定义)。然后每个顶级问题及其子部分不会跨页。\begin{questions}minipage\begin{question}\item\begin{questions}

之所以\ifthenelse使用,是因为连续计数enumitem需要对第一个列表使用与后续列表不同的命令,并且我希望用户避免考虑这一点。

\documentclass{article}
\usepackage[english]{babel} % needed by blindtext
\usepackage[pangram]{blindtext} % to produce dummy text only
\usepackage{enumitem}
\newlist{questions}{enumerate}{3}
\setlist[questions,1]{label*=\textbf{\arabic*}.}
\setlist[questions,2]{label=(\alph{questionsii})}
\setlist[questions,3]{label=(\roman{questionsiii})}

\usepackage{ifthen}
\gdef\questionsstarted{0}
\newenvironment{question}{\ifthenelse{\equal{\questionsstarted}{0}}{\gdef\questionsstarted{1}\begin{minipage}{\textwidth}\begin{questions}[series=questions,start=1,leftmargin=*,resume]}{\begin{minipage}{\textwidth}\begin{questions}[resume*=questions]}}{\end{questions}\end{minipage}\bigskip}

\begin{document}
    ~\vspace{15cm} % to push the text near the page break
    
    \begin{question}
        \item \Blindtext[1][4]
    \end{question}

    \begin{question}
        \item \Blindtext[2][4]
        \begin{questions}
            \item \Blindtext[1][4]
            \item \Blindtext[1][4]
            \item \Blindtext[1][4]
        \end{questions}
    \end{question}
\end{document}

相关内容