朝鲜前端开发者如何应对平台不一致问题

2025-06-07

朝鲜前端开发者如何应对平台不一致问题

你是一名朝鲜工程师,被选中负责开发一个新的政府项目。这是一个HTML表单,朝鲜政治领导人需要填写才能[REDACTED]使用。

其中一个字段要求用户选择他们希望使用的称呼。由于列表可能会很长,你决定使用你之前提到的那个<select>元素。它看起来像这样:

Windows(Chrome) macOS(Safari)

没什么不寻常的,大多数情况下都是完全可以接受的。

你知道它<select>有那种“搜索”功能,只要你输入,它就会跳转到相应的条目。但你不确定伟大领袖是否知道这一点。你觉得这没什么大不了的,只要列表按字母顺序排列就行。

那么移动设备呢?

安卓系统(Chrome) iOS(Safari)

Android 系统会尽可能多地利用屏幕空间,覆盖地址栏。iOS 系统则由于可见项目数量过少,导致列表过大时体验不佳。而且,Android 和 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>
Enter fullscreen mode Exit fullscreen mode
.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;
}
Enter fullscreen mode Exit fullscreen mode
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')
}
Enter fullscreen mode Exit fullscreen mode

重要信息:

  • position: fixed将模式固定到屏幕上;
  • height: 100vh使高度达到视口的 100%;
  • 模态框分为两个部分:标题和正文;
  • 标题的高度由其子项定义,无需明确设置;
  • 主体用填充剩余高度flex: 1
  • scrolly-y: auto当列表不适合时,可以在主体中使其可滚动。

它看起来像这样:

安卓系统(Chrome) iOS(Safari)

在 iOS 上看起来不错,但在 Android 上最后几项被截断了。为什么?

某些移动浏览器会在用户向下滚动时隐藏地址栏。这会改变可见的视口高度,但不会影响 的含义100vh。因此100vh,实际高度会比最初可见的高度略高。

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

太棒了!这已经比移动端原生版本有了提升<select>。现在你需要实现过滤行为。

你肯定不想每次打开模态框后都要费力地触摸筛选输入框。所以你应该focus()模态框打开后立即设置筛选输入框。这样,键盘就会弹出,用户就可以立即开始输入。我们来看看它是什么样子的:

安卓系统(Chrome) iOS(Safari)

这次在 Android 上一切正常。但在 iOS 上,当你尝试滚动列表时,模态框标题会滚动到边界之外。这是怎么回事?

没有键盘的 iOS 带键盘的 iOS

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

更糟糕的是,你根本无法知道键盘的高度,甚至根本不知道它是否存在(用户可能使用的是实体键盘)。任何巧妙的 CSS 技巧都无法帮你节省时间。

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

您可以在列表底部添加一个间隔(为了便于查看,以绿色突出显示)。此间隔的高度等于列表区域的高度减去一个元素。这样,列表始终可以滚动到底部,使最后一个元素位于列表的最顶部。

仍然有方法可以使模态滚动到视口之外,您需要修补它们。

一种方法是滑动当前可见的任何不可滚动元素。在你的例子中,这个元素是模态窗口的头部。你不能直接通过 CSS 禁用所有指针事件,因为你需要确保内部元素(过滤输入框和关闭按钮)仍然可用。解决方案是禁用touchmove事件滚动:

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

header.addEventListener('touchmove', event => {
  event.preventDefault()
})
Enter fullscreen mode Exit fullscreen mode

默认反应touchmove是滚动,因此阻止它将preventDefault()使其无法滚动。


现在让我们稍微绕个弯。为了让文章更具通用性,我一直用 HTML + JavaScript 编写这些示例。但在使用 React 开发时,我遇到了一系列解决方法。以下是我在 React 中定义事件处理器的方法:

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

// …

<Element onTouchMove={handleTouchMove} />

Enter fullscreen mode Exit fullscreen mode

我们可能期望在纯 JavaScript 中,这将转换为如下内容:

const element = document.getElementById('element')

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

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

document.addEventListener('touchmove', event => {
  const element = React.getElementFromEvent(event)

  // call the callback for this element
})
Enter fullscreen mode Exit fullscreen mode

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

浏览器会阻止它。这是 Chrome 更新引入的,该更新默认将事件设置为“被动”,并且无法preventDefault在文档级别阻止这些事件。解决方案是在节点级别手动绑定事件,而不是通过 React 的事件系统进行绑定:

ref = React.createRef();

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

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

// …

<Element ref={ref} onTouchMove={handleTouchMove} />
Enter fullscreen mode Exit fullscreen mode

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

在我写这篇文章的时候,React 的事件系统正在被重写,所以当你读到这篇文章的时候这个问题可能已经不存在了。

现在回到你的问题。


还有一种方法可以让你不再滚动,不再期待。如果用户在没有更多项目可显示的情况下坚持滚动,可以向上滚动视口。这些都不再困扰你,你只需在其中添加另一种解决方法:

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

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

modalBody.addEventListener('scroll', menuScroll)
Enter fullscreen mode Exit fullscreen mode

当滚动到底部时,将列表的滚动位置向外推一个像素。这样,外部滚动就不会被触发。

这个解决方案已经相当完善了,但还有一点需要改进。突然覆盖屏幕的模态框可能会有点突兀。如果大人一不留神被吓到了怎么办?谁来照顾你的孩子?

一个简单的过渡动画可以让它更容易理解。或许你可以把模态框从屏幕底部滑下来?用 CSS 过渡动画很容易实现:

.modal {
  /* ... */

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

.modal.show {
  top: 0;
}
Enter fullscreen mode Exit fullscreen mode

现在,你不再使用 和 来初始化模态框,而是使用 来启动它display: none使用 将其推到视口之外。当模态框设置为可见时,它将平滑地滚动到屏幕顶部。让我们看看结果:top: 0display: flextop: 100vh

安卓系统(Chrome) iOS(Safari)

差一点!Android 又恢复正常了,而 iOS 的模态框一出现就直接弹出去了。看来在模态框动画播放过程中切换键盘不太好。你很有信心,只在动画播放结束后显示键盘应该可以解决这个问题:

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

  // new
  setTimeout(() => {
    filterInput.focus()
  }, 500)
}
Enter fullscreen mode Exit fullscreen mode

很简单。你等待 500 毫秒,也就是过渡时长,然后focus()输入内容,键盘才会弹出。你告诉自己,以后会解决这个问题,比如用事件或者一些高级库,而不是依赖 JS 和 CSS 的值保持一致。但你知道这不会发生。结果:

安卓 iOS

iOS 似乎根本没有聚焦输入。当然,这不可能那么简单。iOS只允许focus事件作为用户交互的直接结果发生setTimeout不是吗?你的解决方法是将“选择标题”按钮变成文本输入框:

<input onfocus="openModal()" readonly=true placeholder="Select a title">
Enter fullscreen mode Exit fullscreen mode

readonly会隐藏插入符号,并确保用户在过渡期间无法在此新输入框中输入任何内容。这样,iOS 会根据第一个focus事件显示键盘,从而允许您在过渡完成后将焦点转移到第二个输入框。

成功了!你终于完成了。你为自己的努力感到自豪,知道你的家人至少还能再活几个月。


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

文章来源:https://dev.to/raicuparta/dealing-with-platform-inconsistcies-as-a-north-korean-front-end-developer-3158
PREV
与同一网站上的人交谈🌎 | Chrome 扩展程序
NEXT
如何在网络上赚钱 如何赚钱并赚取更多收入