在朝鲜,前端开发人员面临着甚至可能不存在的平台不一致问题

您是被选为开发新政府项目的朝鲜工程师。 这是HTML表单,朝鲜政治领导人将出于[REDACTED]目的填写该表单。

字段之一要求用户选择他们偏爱的标题。

由于列表可能会很长 ,因此您决定使用旧的<select >元素。 在Windows(Chrome)上看起来像这样:

没有什么与众不同的,在大多数情况下完全可以接受。

您知道<select> 具有这种“搜索”,可以在您键入时跳转到项目 。 但是您不确定伟大领袖是否意识到这一点。 只要列表按字母顺序排列,您就觉得这没什么大不了的。

手机呢? 这是在Android(Chrome)上的外观:

Android尝试使用尽可能多的屏幕,覆盖地址栏。

这是在iOS(Safari)上:

在iOS上,少量可见项使您在使用较大列表时体验糟糕。 它们都缺少搜索或过滤列表项的方法。

国家反过来吗? 不想冒险,您可以自行处理此事。 您需要可以在移动设备上过滤的东西,并更好地利用屏幕空间。

在桌面平台上,这并不是很难实现的:只是一个自定义下拉菜单,其中包含用于过滤的文本输入。 对于移动设备,您需要一些不同的东西。 让我们专注于移动版本,并假设您将有某种方式来根据平台选择正确的实现。

这是您的移动计划:

全屏模式,顶部有固定文本输入用于过滤,其下方是可滚动项列表。 您的第一个直觉告诉您实现应如下所示:

< button onclick = "openModal()" > Select a title </ button >
< div class = "modal" id = "modal" >
  < div class = "modal-header" >
    < input type = "text" id = "filter-input" >
    < button onclick = "closeModal()" > X </ button >
  </ div >
  < div class = "modal-body" >
    < button > Item 1 </ button >
    < button > Item 2 </ button >
    <!-- remaining items... -->
  </ div >
</ div >
.modal {
  display : none;
  position : fixed;
  top : 0 ;
  left : 0 ;
  height : 100vh ;
  flex-direction : column;
}

.modal .show {
  display : flex;
}

.modal-body {
  flex : 1 ;
  overflow-y : auto;
}
const modal = document .getElementById( 'modal' )
const filterInput = document .getElementById( 'filter-input' )

function openModal ( )  {
  modal.classList.add( 'show' )
  filterInput.focus()
}

function closeModal ( )  {
  modal.classList.remove( 'show' )
}

重要部分:

  • position: fixed以将模态position: fixed在屏幕上;
  • height: 100vh使高度为视口的100%;
  • 情态分为两部分:标题和正文;
  • 标头的高度由其子级定义,无需显式设置;
  • 主体使用flex: 1填充剩余高度flex: 1 ;
  • scroll-y :当列表不适合时自动在正文中滚动。

在iOS上可以正常使用:

在Android上,最后一项已被删除:

为什么?

当用户向下滚动时,某些移动浏览器会隐藏地址栏。 这会更改可见的视口高度,但不会更改100vh的含义。 因此100vh实际上比最初看到的要高。

您的模态的position: fixed ,因此您不需要使用vh单位。 height: 100%将正确填充可用高度:

整齐! 这已经是对移动设备上的<select>本地版本的改进。 现在,您需要实现过滤器行为。

您非常确定,您的Guiding Sun Ray不会遇到每次打开模态后每次都必须触摸过滤器输入的麻烦。 因此,模态打开后,您应该focus()过滤器输入。 这样,键盘弹出,用户可以立即开始键入。

在Android上一切看起来都不错:

在iOS上,一旦您尝试滚动列表,就会将模式标头滚动到边界之外:

这是怎么回事?

当您按“领导者”筛选时,列表会变得足够小以适合屏幕而不滚动,但前提是键盘不可见。 在Android上,打开键盘会将视口缩小到可见区域。 但是在iOS上,视口大小保持不变; 它只是被键盘覆盖 。 iOS可让您在键盘打开时滚动页面,以显示页面的缺失部分。 这种行为可能会破坏position: fixed像您这样的position: fixed元素。

更糟糕的是,无法知道键盘的高度,或者根本不知道键盘的高度(用户可以使用硬件键盘)。 没有巧妙的CSS技巧可以节省您的时间。

因此,您需要一个可滚动的列表,其中所有项目都可以访问,而又不知道屏幕下部的任意部分是否可见。 这是您的解决方法:

您在列表的底部添加了一个空格(以绿色突出显示以提高可见性)。 该分隔符的高度是列表区域的高度减去一个元素。 这样,始终可以一直滚动到底部,将最后一个元素移到列表的顶部。

还有一些方法可以使模态滚动在视口之外,您需要对其进行修补。

一种方法是在当前可见的所有不可滚动元素上滑动。 在您的情况下,这就是模式标题。 您不能仅通过CSS禁用所有指针事件,因为您需要内部元素(过滤器输入和关闭按钮)仍然可用。 解决方案是禁用touchmove事件上的滚动:

const header = document .getElementById( 'modal-header' )

header.addEventListener( 'touchmove' , event => {
  event.preventDefault()
})

touchmove的默认反应是滚动,因此使用preventDefault()进行阻止将使其不可滚动。

现在让我们绕个小弯路。 我一直在用HTML + JavaScript编写这些示例,以使本文更加通用。 但是在React开发中,我遇到了这种变通方法。 这就是我在React中定义事件处理程序的方式:

function handleTouchMove ( event )  {
  event.preventDefault()
}

// …

<Element onTouchMove={handleTouchMove} />

期望可能是在普通JavaScript中,这将转换为如下形式:

const element = document .getElementById( 'element' )

element.addEventListener( 'touchmove' , event => {
  // call the callback for this element
})

但是发生的事情更接近于此(不是真正的代码):

document .addEventListener( 'touchmove' , event => {
  const element = React.getElementFromEvent(event)
  
  // call the callback for this element
})

React在文档级别绑定事件,而不是在每个单独节点级别绑定事件。 当我尝试preventDefault() React中的preventDefault()触摸事件时,会发生以下情况:

浏览器将其阻止。 该功能是通过Chrome浏览器更新引入的,该更新默认情况下将事件设置为“被动” ,并且在文档级别无法通过preventDefault阻止preventDefault 。 解决方案是在节点级别手动绑定事件,而不是通过React的事件系统来完成:

ref = React.createRef();

componentDidMount() {
  ref.addEventListener( 'touchmove' , handleTouchMove)
}

function handleTouchMove ( event )  {
  event.preventDefault()
}

// …

<Element ref={ref} onTouchMove={handleTouchMove} />

所以是的,尤其是在React中,这种解决方法需要一种解决方法。

在我撰写本文时, React的事件系统正在被重写 ,因此在您阅读本文时,该问题可能不再存在。

现在回到您的问题。

还有另一种方法可以使您的希望和梦想消失。 如果用户在没有其他要显示的项目时坚持滚动,则可以将视口向上滚动。 这些都不再让您困惑,您只需在其中添加另一个解决方法:

const modalBody = document .getElementById( 'modal-body' )

menuScroll = () => {
  if (modalBody.scrollHeight - modalBody.scrollTop === modalBody.clientHeight) {
    modalBody.scrollTop -= 1
  }
}

modalBody.addEventListener( 'scroll' , menuScroll)

当滚动到达底部时,将列表的滚动位置推离边缘一像素。 这样,永远不会触发外部滚动。

该解决方案已经非常可靠,但是您还需要改进一件事。 突然覆盖屏幕的模态可能有点刺耳。 如果阁下不注意而被吓到怎么办? 谁来照顾您的孩子?

一个简单的过渡动画可以使其更容易跟随。 也许您可以从屏幕底部滑动模态? 使用CSS过渡轻松实现:

.modal {
  /* ... */

  display : flex;
  top : 100vh ;
  transition : top 500ms ;
}

.modal .show {
  top : 0 ;
}

现在,无需使用display: nonetop: 0初始化模态,而是已经使用display: flex启动了模态,但使用top: 100vh将其推到了视口100vh 。 模态设置为可见时,它将平滑滚动到屏幕顶部。 让我们看看在Android上的结果:

Android再次表现良好! 现在在iOS上:

一旦可见,iOS就会将模态爆炸到外层空间。 好像在对模态进行动画处理时切换键盘不是一个好主意。 您非常有信心,只有在完成动画后才显示键盘才能解决该问题:

function openModal ( )  {
  modal.classList.add( 'show' )

  // new
  setTimeout( () => {
    filterInput.focus()
  }, 500 )
}

很简单。 您需要等待500毫秒(与转换持续时间相同),然后focus()输入进行focus()使键盘弹出。 您告诉自己,稍后将清理此事件,也许使用事件或某些精美的库,而不是依赖JS和CSS之间的值一致。 但是你知道这不会发生。 结果:

现在,iOS似乎根本不关注输入。 当然,不可能那么容易。 iOS仅允许焦点事件是用户交互的直接结果 ,而setTimeout并非如此。 解决方法是将“选择标题”按钮转换为文本输入:

< input onfocus = "openModal()" readonly = true placeholder = "Select a title" >

readonly隐藏插入符号,并确保用户在过渡期间无法在此新输入中键入任何内容。 这样,iOS将基于第一个焦点事件显示键盘,从而使您可以在转换完成后将焦点更改为第二个输入。

而且有效! 您终于完成了。 您会为自己的工作感到自豪,因为知道您的家人至少还会再住几个月。

在此处找到模态的完整代码

From: https://hackernoon.com/dealing-with-platform-inconsistencies-as-a-north-korean-front-end-developer-frdm3rp6

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值