我刚开始接触python/flask.我做了一个简单的应用程序,它收集用户对5个变量的输入,并显示依赖于这6个变量组合的输出.我用以下代码制作了一个简单的网页,通过可点击的按钮收集用户的输入:

function setValue(inputId, value, button) {
  document.getElementById(inputId + '_input').value = value;
}
body {
  width: 35em;
  margin: 0 auto;
  font-family: Tahoma, Verdana, Arial, sans-serif;
}

.button {
  align-self: center;
  background-color: #fff;
  background-image: none;
  background-position: 0 90%;
  background-repeat: repeat no-repeat;
  background-size: 4px 3px;
  border-radius: 15px 225px 255px 15px 15px 255px 225px 15px;
  border-style: solid;
  border-width: 2px;
  box-shadow: rgba(0, 0, 0, .2) 15px 28px 25px -18px;
  box-sizing: border-box;
  color: #41403e;
  cursor: pointer;
  display: inline-block;
  font-family: Neucha, sans-serif;
  font-size: 1rem;
  line-height: 23px;
  outline: none;
  padding: .75rem;
  text-decoration: none;
  transition: all 235ms ease-in-out;
  border-bottom-left-radius: 15px 255px;
  border-bottom-right-radius: 225px 15px;
  border-top-left-radius: 255px 15px;
  border-top-right-radius: 15px 225px;
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;
}

.button:hover {
  box-shadow: rgba(0, 0, 0, .3) 2px 8px 8px -5px;
  transform: translate3d(0, 2px, 0);
}

.button:focus {
  box-shadow: rgba(0, 0, 0, .3) 2px 8px 4px -6px;
}
<h1>Rhesus Genotype Analyzer</h1>
<form method="POST" action="/">
  <label for="D">D:</label>

  <button type="button" class="button" onclick="setValue('D', 'pos', this)">Pos</button>
  <button type="button" class="button" onclick="setValue('D', 'neg', this)">Neg</button>
  

  <label for="C">C:</label>
  <button type="button" class="button" onclick="setValue('C', 'pos', this)">Pos</button>
  <button type="button" class="button" onclick="setValue('C', 'neg', this)">Neg</button>
  <br><br>

  <label for="E">E:</label>
  <button type="button" class="button" onclick="setValue('E', 'pos', this)">Pos</button>
  <button type="button" class="button" onclick="setValue('E', 'neg', this)">Neg</button>
  <br><br>

  <label for="c">c:</label>
  <button type="button" class="button" onclick="setValue('c', 'pos', this)">Pos</button>
  <button type="button" class="button" onclick="setValue('c', 'neg', this)">Neg</button>
  <br><br>

  <label for="e">e:</label>
  <button type="button" class="button" onclick="setValue('e', 'pos', this)">Pos</button>
  <button type="button" class="button" onclick="setValue('e', 'neg', this)">Neg</button>
  <br><br>

  <input type="hidden" id="D_input" name="D">
  <input type="hidden" id="C_input" name="C">
  <input type="hidden" id="E_input" name="E">
  <input type="hidden" id="c_input" name="c">
  <input type="hidden" id="e_input" name="e">

  <input type="submit" value="Submit">

我的问题是,10个按钮中只有一个按钮显示了"焦点"状态.是否可以单独处理每行按钮?也就是说,允许‘D’位置或否定按钮‘聚焦’,并允许‘E’位置或否定也‘聚焦’,以便用户可以看到他们 Select 了什么?

非常感谢任何帮助!

推荐答案

一种方法如下,尽管这个答案依赖于:has()css伪类函数,尽管它在现代浏览器中得到了很好的支持,但这种方法可能意味着您将需要根据您的用户使用的浏览器来使用JavaScript.

也就是说,以下代码满足了您的需要(我认为),并且只使用了HTML和CSS;代码中有解释性注释:

/*
  a simple reset, in which I ensure that all elements are sized
  in the same way, following the border-box algorithm which
  includes assigned padding, margins, and border-width within
  the assigned size of the element. I'm also setting the font
  of the document to remove browser defaults:
*/
*,
::before,
::after {
  box-sizing: border-box;
  font-family: system-ui;
  font-size: 1rem;
  font-weight: 400;
  margin: 0;
  padding: 0;
}

/* forcing the <html> element to occupy the full size of the
   viewport's block-axis (the axis upon which 'blocks' are
   positioned according to the language choice of the user): */
html {
  block-size: 100%;
}

/* defining some custom properties to help lay out the contents,
   and its spacing: */
body {
  --space-s: calc(var(--space) * 0.5);
  --space: 0.5rem;
  --space-l: calc(var(--space) * 2);
  /* 1vb is 1% of the element's size on the block-axis, therefore
     this rule sizes the body to take up the full size of the
     viewport: */
  min-block-size: 100vb;
  /* setting the padding of the element, using the CSS var()
     function along with the defined CSS custom property: */
  padding: var(--space-l);
}

main {
  /* as above, sizing the block-axis of the element to take 100%
     of the available space: */
  min-block-size: 100%;
  border: 1px solid currentColor;
  padding: var(--space-l);
}

h1 {
  /* styling the font of the <h1> after forcing all
     fonts (earlier) to be only 1rem, here we double
     that size to emphasise the heading: */
  font-size: 2rem;
  /* setting a margin on the block-end (the side of
     the element that would be nearest the next block-
     level sibling element) to be in proportion to
     the font-size of the current element: */
  margin-block-end: 0.5em;
}

h2::after {
  content: ':';
}

form {
  /* using grid layout: */
  display: grid;
  /* setting the gap between adjacent elements within
     the grid: */
  gap: var(--space-l);
}

/* styling any <button> element within any <form> that
   has any :invalid form-elements (<input>, <textarea>...): */
form:has(:invalid) button {
  /* highlighting that clicking the button is disallowed: */
  cursor: not-allowed;
  /* reducing the opacity to imply that the element is
     disabled: */
  opacity: 0.5;
  /* it would have been possible to use:
        pointer-events: none;
     but unfortunately that prevents the :hover pseudo-class
     from matching, which means the cursor wouldn't be
     changed. This is the choice I made, you may feel
     that preventing user-action on the <button> is worth
     that limitation. */
}

fieldset {
  --indicator: hsl(0deg 60% 40% / 0.8);
  align-items: center;
  display: flex;
  flex-flow: row wrap;
  gap: var(--space-l);
  isolation: isolate;
  padding-block: var(--space);
  padding-inline: var(--space-l);
  position: relative;
}

fieldset::after {
  /* I freely admit I went a bit overboard...
     here we're using a linear-gradient as a background image,
     the gradient running 90degrees (to the right, 0degrees is
     vertical), we have transparent from 0 running to 70% of the
     background-size, after which we use the colour assigned to
     the custom CSS property --indicator or, if that property isn't
     defined, the default colour of transparent.
     The background is positioned at 100% (x-axis) and 50% (y-axis),
     and sized 100% in both horizontal and vertical axes (respectively),
     and we set no-repeat so that the image doesn't repeat: */
  background: linear-gradient(90deg, transparent 0 70%, var(--indicator, transparent)) 100% 50% / 100% 100% no-repeat;
  /* to occlude the error gradient we use the clip-path, which allows
     us to limit the visibility of the element according to the
     shape we define. We're using the polygon() function here,
     which takes a list of comma-separated positions (for x and y
     respectively), to define a number of points; within the space
     formed by those points the content is visible. Outside, the
     content is hidden ('clipped'): */
  clip-path:
    polygon(
      100% 0,
      100% 0,
      100% 100%,
      100% 100%
    );
  /* content is a mandatory property to have the pseudo-element
     be rendered, an empty string is valid 'content' for this: */
  content: '';
  /* using inset to specify the position of the pseudo-element in
     reference to its containing block, here it's positioned 0
     distance from each edge (top, right, bottom, left respectively)
   */
  inset: 0;
  position: absolute;
  /* we're transitioning the clip-path property, over 500ms and
     with a linear easing function: */
  transition: clip-path 500ms linear;
  /* to position the pseudo-element behind all content of its
     containing block ancestor, appearing in front of only
     that element's background: */
  z-index: -1;
}

/* styling the ::after pseudo-element of any <fieldset> that contains
   a :user-invalid form-element: */
fieldset:has(:user-invalid)::after {
  clip-path: polygon( 0 0, 100% 0, 100% 100%, 0 100%);
}

label {
  /* using a non-'static' (the default) value for the
     position property, in order that this element becomes
     the containing block for its descendants: */
  position: relative;
}

/* the element that contains the text associated with each
   <input> element: */
.labelText {
  /* various CSS custom properties: */
  --base-h: 0deg;
  --base-s: 20%;
  --base-l: 30%;
  --base-z-pos: 0px;
  --border-color: hsl(var(--base-h) var(--base-s) var(--base-l) / 1);
  --offset-x: 0px;
  --offset-y: 6px;
  --spread: 0.3rem;
  /* to ensure that the element is sized to have equal size on both
     its block, and inline, axes: */
  aspect-ratio: 1;
  /* a subtle background, I left it in but it's not all that visible: */
  background: repeating-linear-gradient(135deg, snow 0 0.5rem, azure 0.5rem 0.75rem);
  border: 2px solid var(--border-color);
  /* defining the block-size of the element, which will influence the
     inline-size via the aspect-ratio property: */
  block-size: 3.5rem;
  display: grid;
  /* using drop-shadow in place of box-shadow, as it's a little more accurate
     (if you remove the background of this element, the visible content of the
     element generates an accurate shadow, whereas box-shadow gives a shadow to
     the outer shape of the element, effectively just a rectangular polygon): */
  filter: drop-shadow(var(--offset-x) var(--offset-y) var(--spread) var(--border-color));
  /* centering the content within the element: */
  place-content: center;
  /* using perspective and translateZ() to move the element somewhat
     faithfully in 3d 'space': */
  transform: perspective(500px) translateZ(var(--base-z-pos));
  /* required to enable any sort of accuracy within the 3d space: */
  transform-style: preserve-3d;
  /* defining a transition to apply to every (animatable) property: */
  transition: all 300ms ease-in-out;
  /* defining the precise properties to transition: */
  transition-property: transform, filter;
}

/* updating CSS properties for:
   any .labelText element when hovered: */
.labelText:hover,
/* any .labelText element that is immediately preceded
   by an <input> which is in any of the states listed
   within the :is() pseudo-class function: */
input:is(:active, :focus, :checked) + .labelText {
  --base-h: 120deg;
  --base-z-pos: -50px;
}

/* as above, but to specify different properties that
   I didn't want available to the .labelText:hover: */
input:is(:active, :focus, :checked) + .labelText {
  --base-s: 40%;
  --base-l: 60%;
  --base-z-pos: -80px;
}

/* we're hiding the <input>, since we're "replacing" it
   with the <span>: */
input {
  /* scaling it very, very, very small: */
  scale: 0.01;
  /* using absolute position to remove the element from
     the document flow: */
  position: absolute;
}

.error {
  background-color: snow;
  border: 2px solid currentColor;
  /* using a large border-radius to have the browser
     style the borders into a 'pill' shape, with
     rounded ends: */
  border-radius: 5rem;
  /* using inset to position the element with reference
     to the containing block, here the 'auto' values
     are equivalent to not setting a property and allowing
     the browser to calculate it, but all values must be
     provided so we have to specify a value; -150% positions
     the pseudo-element's right side 150% outside of the
     containing block. Negative numbers move away from the
     center of the containing block, positive numbers
     move towards the center (on the relevant axis): */
  inset: auto -150% auto auto;
  padding-block: var(--space-s);
  padding-inline: var(--space);
  position: absolute;
  transition: inset 500ms linear;
}

/* :user-invalid matches form-elements that have been given,
   or retained invalid values due to the action/inaction of
   the user; the pseudo-class only matches elements after
   the user attempts to submit the form; with this selector
   we're seleing any .error message within a <fieldset> that
   itself contains an <input> which is invalid following the
   user's attempt to submit the form: */
fieldset:has(input:user-invalid) .error {
  inset: auto 20% auto auto;
}
<main>
  <h1>Rhesus Genotype Analyzer</h1>
  <form method="POST" action="/">
    <fieldset>
      <!-- I opted to use an <h2> element to label the grouped elements,
           as a <legend> wouldn't be styled as you seem to wish: -->
      <h2>D</h2>
      <!-- using <label> elements to associate the text, in the nested <span>
           with the <input>, no "for" attribute is required as the relationship
           is implicit due to the <input> being nested within the <label> -->
      <label>
        <!-- the <span> follows the <input> in order to use
             the validity, and user-interactivity, pseudo-
             classes of the <input> to style the adjacent
             <span>: -->
        <input type="radio" value="pos" name="D" required="">
        <span class="labelText">pos</span>
      </label>
      <label>
        <input type="radio" value="neg" name="D">
        <span class="labelText">neg</span>
      </label>
      <!-- using a <span> to contain an error message in the event that the
           user tries to submit an incomplete form: -->
      <span class="error">This field is mandatory, please select one of the options.</span>
    </fieldset>

    <fieldset>
      <h2>C</h2>
      <label>
        <input type="radio" value="pos" name="C" required="">
        <span class="labelText">pos</span>
      </label>
      <label>
        <input type="radio" value="neg" name="C">
        <span class="labelText">neg</span>
      </label>
      <span class="error">This field is mandatory, please select one of the options.</span>
    </fieldset>

    <fieldset>
      <h2>E</h2>
      <label>
        <input type="radio" value="pos" name="E" required="">
        <span class="labelText">pos</span>
      </label>
      <label>
        <input type="radio" value="neg" name="E">
        <span class="labelText">neg</span>
      </label>
      <span class="error">This field is mandatory, please select one of the options.</span>
    </fieldset>

    <fieldset>
      <h2>c</h2>
      <label>
        <input type="radio" value="pos" name="c" required="">
        <span class="labelText">pos</span>
      </label>
      <label>
        <input type="radio" value="neg" name="c">
        <span class="labelText">neg</span>
      </label>
      <span class="error">This field is mandatory, please select one of the options.</span>
    </fieldset>

    <fieldset>
      <h2>e</h2>
      <label>
        <input type="radio" value="pos" name="e" required="">
        <span class="labelText">pos</span>
      </label>
      <label>
        <input type="radio" value="neg" name="e">
        <span class="labelText">neg</span>
      </label>
      <span class="error">This field is mandatory, please select one of the options.</span>
    </fieldset>
    
    <!-- rather than use an <input> to submit the form, I opted to use
         a <button>; the default action of a <button> within a <form> is
         to submit the form, which is why there's no explicit 'submit'
         attribute: -->
    <button>Submit</button>
  </form>
</main>

JS Fiddle demo.

参考资料:

Html相关问答推荐

如何改进这个jQuery滑动器的功能?

如何将数据发送到Flask 应用程序中的表单文本框

一次悬停可触发同一分区中的另一次悬停

使用固定的HTML代码创建两个可向右滚动的行

悬停时跳转的内容

Tumblr博客:如何修复提交页面和询问页面框列计数问题?

如何防止第二列中的内容影响第一列中内容的位置?

Angular 15 p-对话框不使用组件 Select 器呈现HTML

如何在伪元素背景中定位多个CSS径向梯度

嵌套表列高度不是100%高度的问题

Firefox和Chrome在文字装饰和文字阴影方面的不同优先顺序

如何更改Django中的默认按钮?

可以';t使我的导航栏在HTML/CSS中正确对齐

如何使粘性导航栏能够跨其他组件工作?

从垫分页器中删除输入边框

如何使用 html 和 css 为卡片创建此背景框架

如何在单击按钮时切换 Blazor 中的类?

自定义进度条 Html 和 CSS 布局

如何使列表的第一个元素比 css 中的其他元素大?

尽管设置了 width:100% left:0,但居中对齐的 CSS 动画并不对称