【问题标题】:How do I prevent a pseudo-element or input from widening a flex item in a flexbox?如何防止伪元素或输入扩大弹性框中的弹性项目?
【发布时间】:2017-09-06 12:40:41
【问题描述】:

我已经创建了一个 flexbox 容器,它充当具有两个输入字段的表单控件,表示两个日期之间的范围。如果用户在两个字段中的任何一个字段中输入了内容,则该字段旁边会出现一个叉号。用户可以点击这个十字来清除该字段。

十字架是一个:after 伪元素,使用一些漂亮的css 和JavaScript 创建并与之交互,主要基于this answer on Stack Overflow。这是我的实现(有些简化,但不多):

$('.field.clearable').on('change input', 'input', function($e) {
    $(this).parent().toggleClass('non-empty', !!this.value);
  })
  .on('mousemove', 'span.non-empty', function($e) {
    var $this = $(this);
    $this.toggleClass('onX', $this.width() < $e.clientX - $this.offset().left);
    $this.hasClass('onX') ? $this.attr('title', 'Clear field') : $this.removeAttr('title');
  })
  .on('mouseleave', 'span.non-empty', function($e) {
    var $this = $(this);
    $this.removeClass('onX')
      .removeAttr('title');
  })
  .on('click', 'span.non-empty.onX', function($e) {
    $e.preventDefault();
    var $this = $(this);
    $this.removeClass('non-empty onX')
      .removeAttr('title')
      .children('input')
      .val('')
      .trigger('clear');
  });
/* reset and cosmetic css */

* {
  margin: 0;
  padding: 0;
  color: inherit;
  font-family: inherit;
  font-size: inherit;
}

::-webkit-input-placeholder {
  color: #ccc;
  font-size: 80%;
  opacity: .8;
}

:-moz-placeholder {
  color: #ccc;
  font-size: 80%;
  opacity: .8;
}

::-moz-placeholder {
  color: #ccc;
  font-size: 80%;
  opacity: .8;
}

:-ms-input-placeholder {
  color: #ccc;
  font-size: 80%;
  opacity: .8;
}

:focus::-webkit-input-placeholder {
  opacity: .5;
}

:focus:-moz-placeholder {
  opacity: .5;
}

:focus::-moz-placeholder {
  opacity: .5;
}

:focus:-ms-input-placeholder {
  opacity: .5;
}

html {
  font-family: Arial;
  font-size: 10px;
  font-weight: normal;
  text-align: left;
  height: 100%;
}

body {
  margin: 4em auto;
  width: 400px;
  color: #2c5ba0;
  font-size: 1.5rem;
  line-height: 1.5em;
  background-color: #fff;
}


/* relevant css */

div.field {
  display: inline-block;
  margin: .4em 0;
}

div.field>label {
  display: block;
  margin: 0 0 .3em .2em;
  font-size: 80%;
  line-height: 1em;
}

div.field>span {
  position: relative;
  display: -webkit-flex;
  display: flex;
  -webkit-align-items: center;
  align-items: center;
  -webkit-justify-content: space-between;
  justify-content: space-between;
  box-sizing: border-box;
  height: 2em;
  border: .1em solid #ddd;
  background-color: #fff;
}

div.field>span>span {
  -webkit-flex: 1;
  flex: 1;
}

div.field>span>span>input {
  width: 100%;
  border: none;
}

div.field.range>span input {
  text-align: center;
}

div.field.range>span label {
  -webkit-flex: 0;
  flex: 0;
  margin: 0 .2em;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

div.field.clearable span.non-empty {
  position: relative;
  padding-right: 2em;
}

div.field.clearable span.non-empty:after {
  position: absolute;
  right: 0;
  top: 0;
  content: 'x';
  display: inline-block;
  width: 2em;
  height: 2em;
  color: #ddd;
  vertical-align: middle !important;
  text-align: center !important;
  font-style: normal !important;
  font-weight: normal !important;
  text-transform: none !important;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  cursor: pointer;
}

div.field.clearable span.non-empty.onX:after {
  color: #f37e31;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="field clearable range">
  <label for="start">created</label>
  <span>
    <span>
      <input type="text" id="start" placeholder="from">
    </span>
    <label for="end">–</label>
    <span>
      <input type="text" id="end" placeholder="until">
    </span>
  </span>
</div>

当两个&lt;input&gt; 字段都包含值并因此在它们旁边有一个叉号时,它们的宽度比很好地相互抵消,没有任何问题。但是,当只有一个 &lt;input&gt; 字段包含值并且叉号仅出现在该 &lt;input&gt; 字段旁边时,叉号/&lt;input&gt; 字段会将其他内容推到一边。

这不应该发生。我怀疑&lt;input&gt; 字段是罪魁祸首。即使容器div.field 应该保持其display: block 的灵活性,内部内容的比率也应该保持不变。

以下是我认为可以通过以下方式实现的相关位:

/* the span that acts as the flexbox container */
div.field>span {
  position: relative;
  display: -webkit-flex;
  display: flex;
  -webkit-align-items: center;
  align-items: center;
  -webkit-justify-content: space-between;
  justify-content: space-between;
  box-sizing: border-box;
  height: 2em;
  border: .1em solid #ddd;
}

/* let the spans, containing the <input> fields, take available space */
div.field>span>span {
  -webkit-flex: 1;
  flex: 1;
}

/*
  let <input> fields take 100% content space of span

  I thought this should take into account any padding on its parent
*/
div.field>span>span>input {
  width: 100%;
  border: none;
}

/* let center label (the dash) not be flexible */
div.field.range>span label {
  -webkit-flex: 0;
  flex: 0;
  margin: 0 .2em;
}

/* if .non-empty class was set by JavaScript */

/*
  make room for :after by setting padding-right accordingly

  but this is the issue:
  I thought this would also make the <input> shrink accordingly,
  but it appears it doesn't shrink the amount I anticipated
*/
div.field.clearable span.non-empty {
  position: relative;
  padding-right: 2em;
}

/* show :after pseudo-element */
div.field.clearable span.non-empty:after {
  position: absolute;
  right: 0;
  top: 0;
  content: 'x';
  display: inline-block;
  width: 2em;
  height: 2em;
  /* etc. */
}

你知道我怎样才能让整个控制元素保持其灵活性,包括它的:after 诡计,但在出现其中一个十字时让&lt;input&gt;s 相应地缩小?

或者更简单地说,我想:有没有办法让两个弹性项目,flex: 1,在任何给定时间都占用相等的空间?


我之所以使用 :after 元素而不是 background-image 就像我从中获得灵感的原始答案一样,是因为我使用的是自定义图标字体而不是图标图像。

【问题讨论】:

  • 伪类应该去哪里? ...在输入之上?
  • @LGSon 不,伪(包含输入的&lt;span&gt; 的子项)应该放在输入旁边(否则输入文本可能会出现在十字下方,我不想发生这种情况)。伪是2em 宽,因此我通过在&lt;span&gt; 上设置padding-right: 2em 为它腾出空间。我认为输入会相应地缩小,但它似乎没有这样做。
  • @LGSon 你实际上让我重新考虑它。我刚刚意识到无论我当前的问题如何,文本似乎都会随着填充而微移,因为文本是居中对齐的。但是,如果我放弃填充并在伪元素上设置 background-color 并将其放在文本字段的顶部,我的问题可能会得到解决。不过,我将不得不调整检测是否点击十字的 JavaScript 来解决这个问题。但谢谢你的问题。这帮助我获得了一个新的视角。干杯!

标签: html css input flexbox width


【解决方案1】:

如果您删除 padding-right 并调整 input 的大小,它将起作用

你需要修复点击处理程序

div.field.clearable span.non-empty {
  position: relative;
}

div.field.clearable span.non-empty input {
  width: calc(100% - 2em);
}

$('.field.clearable').on('change input', 'input', function($e) {
    $(this).parent().toggleClass('non-empty', !!this.value);
  })
  .on('mousemove', 'span.non-empty', function($e) {
    var $this = $(this);
    $this.toggleClass('onX', $this.width() < $e.clientX - $this.offset().left);
    $this.hasClass('onX') ? $this.attr('title', 'Clear field') : $this.removeAttr('title');
  })
  .on('mouseleave', 'span.non-empty', function($e) {
    var $this = $(this);
    $this.removeClass('onX')
      .removeAttr('title');
  })
  .on('click', 'span.non-empty.onX', function($e) {
    $e.preventDefault();
    var $this = $(this);
    $this.removeClass('non-empty onX')
      .removeAttr('title')
      .children('input')
      .val('')
      .trigger('clear');
  });
/* reset and cosmetic css */

* {
  margin: 0;
  padding: 0;
  color: inherit;
  font-family: inherit;
  font-size: inherit;
}

::-webkit-input-placeholder {
  color: #ccc;
  font-size: 80%;
  opacity: .8;
}

:-moz-placeholder {
  color: #ccc;
  font-size: 80%;
  opacity: .8;
}

::-moz-placeholder {
  color: #ccc;
  font-size: 80%;
  opacity: .8;
}

:-ms-input-placeholder {
  color: #ccc;
  font-size: 80%;
  opacity: .8;
}

:focus::-webkit-input-placeholder {
  opacity: .5;
}

:focus:-moz-placeholder {
  opacity: .5;
}

:focus::-moz-placeholder {
  opacity: .5;
}

:focus:-ms-input-placeholder {
  opacity: .5;
}

html {
  font-family: Arial;
  font-size: 10px;
  font-weight: normal;
  text-align: left;
  height: 100%;
}

body {
  margin: 4em auto;
  width: 400px;
  color: #2c5ba0;
  font-size: 1.5rem;
  line-height: 1.5em;
  background-color: #fff;
}


/* relevant css */

div.field {
  display: inline-block;
  margin: .4em 0;
}

div.field>label {
  display: block;
  margin: 0 0 .3em .2em;
  font-size: 80%;
  line-height: 1em;
}

div.field>span {
  position: relative;
  display: -webkit-flex;
  display: flex;
  -webkit-align-items: center;
  align-items: center;
  -webkit-justify-content: space-between;
  justify-content: space-between;
  box-sizing: border-box;
  height: 2em;
  border: .1em solid #ddd;
  background-color: #fff;
}

div.field>span>span {
  -webkit-flex: 1;
  flex: 1;
}

div.field>span>span>input {
  width: 100%;
  border: none;
}

div.field.range>span input {
  text-align: center;
}

div.field.range>span label {
  -webkit-flex: 0;
  flex: 0;
  margin: 0 .2em;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

div.field.clearable span.non-empty {
  position: relative;
}

div.field.clearable span.non-empty input {
  width: calc(100% - 2em);
}

div.field.clearable span.non-empty:after {
  position: absolute;
  right: 0;
  top: 0;
  content: 'x';
  display: inline-block;
  width: 2em;
  height: 2em;
  color: #ddd;
  vertical-align: middle !important;
  text-align: center !important;
  font-style: normal !important;
  font-weight: normal !important;
  text-transform: none !important;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  cursor: pointer;
}

div.field.clearable span.non-empty.onX:after {
  color: #f37e31;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="field clearable range">
  <label for="start">created</label>
  <span>
    <span>
      <input type="text" id="start" placeholder="from">
    </span>
    <label for="end">–</label>
    <span>
      <input type="text" id="end" placeholder="until">
    </span>
  </span>
</div>

【讨论】:

  • 是的,这实际上也是我最初的问题的一个可行的解决方案。不过,我将不得不摆弄一下尺寸; 2em 实际上似乎有点多,现在我已经尝试过了,但这与实际问题无关。再次感谢!
  • @DecentDabbler 很高兴帮助一位开发人员
【解决方案2】:

但是,当只有一个字段包含值并且 十字仅出现在该字段旁边,十字 / 字段将其他内容推到一边。

这不应该发生。我怀疑这些领域是 罪魁祸首。即使容器 div.field 应保持其显示: 块的灵活性,内部内容的比例应该保持不变 常数。

这是因为您的元素是为关闭按钮技巧而构造的。通常padding 上的spans 会导致弹性项目增加或丢失它们的大小并影响弹性布局。但是,如果您删除填充,那么您将很难计算点击关闭的偏移量。

此外,您必须继续修改宽度并计算鼠标光标在 mousemove 上的位置,以保持关闭按钮正常工作。

可能的更好解决方案之一是在inputs 上使用填充,并仅使用伪元素的位置和不透明度来计算关闭按钮的外观和消失。这将使flex 正常工作。

然后,您只需检查跨度上的点击,而不是跟踪鼠标移动。

示例小提琴(无 jQuery):http://jsfiddle.net/abhitalks/4fnvukc2/

示例片段:

var closers = document.querySelectorAll('.closer'), 
    inputs = document.querySelectorAll('.closer input');

for (i=0; i < closers.length; i++) {
  closers[i].addEventListener('click', clearer);
}
for (i=0; i < inputs.length; i++) {
  inputs[i].addEventListener('input', closer);
}

function clearer(e) {
  if (e.target.tagName == 'SPAN') {
    e.target.firstElementChild.value = '';
    e.target.classList.remove('dirty');
  }
}
function closer(e) {
  e.stopPropagation();
  e.target.parentElement.classList.add('dirty');
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-size: 1em; font-family: sans-serif;}
div.field { 
  margin: 4em; 
  display: flex; justify-content: space-between;
}
span.closer {
  display: inline-block; position: relative;
  flex: 1 1 auto;
}
span.sep { flex: 0 1 auto; margin: 0 1em;}
span.closer::before {
  content: '×'; font-weight: bold;
  position: absolute; 
  right: -10px; top: 4px; opacity: 0;
  transition: all 0.2s ease-out;
}
span.closer.dirty::before { 
  right: 6px; opacity: 1; cursor: pointer;
}
span.closer.dirty:hover::before { color: #00f; }
.closer input { padding: 3px 16px 3px 4px; width: 100%; }
::-webkit-input-placeholder { color: #ccc; }
:-moz-placeholder { color: #ccc; }
<div class="field range">
  <span class="closer">
    <input type="text" id="start" placeholder="from" />
  </span>
  <span class="sep">–</span>
  <span class="closer">
    <input type="text" id="end" placeholder="until" />
  </span>    
</div>

【讨论】:

  • 这看起来是一个非常漂亮和干净得多的解决方案(比我自己最初的诡计,我应该添加:))。谢谢! mousemove 诡计实际上困扰着我。只是为了我的理解:e.target.tagName == 'SPAN' 是否会产生 true 因为 ::before 被 DOM 视为 &lt;span&gt; 的一部分?
  • 是的@DecentDabbler,它是跨度的一部分。
  • @LGSon 是的,尽管我非常感谢您的意见,但我可能不得不选择这个作为最佳答案。这看起来真的很干净。
  • @DecentDabbler 我也会选择这个:)
  • LGSon 是第一个发现问题的人。确实感谢@LGSon!'
猜你喜欢
  • 2015-02-18
  • 1970-01-01
  • 2016-02-26
  • 2018-09-07
  • 1970-01-01
  • 2019-04-29
  • 1970-01-01
  • 2016-07-13
  • 2017-09-07
相关资源
最近更新 更多