分类
css

CSS 网页保持全屏并自动伸长

其实是个小需求,以前也搞过很多次,没想到前几天被坑了一下,记一笔。

以前,如果想要页面在内容任意多的时候都能占满浏览器,可以简单设置:

html,
body {
  height: 100%;
}

但是这样设置,在 Safari 浏览器上,会将 <body> 固定为窗口高度,如果内容多,就会被底部挡住内容。解决方案是 body 高度用 min-height:100%

如果是三行一列的结构,即上面是导航条,下面是脚部,中间随内容自适应,可以用:

body {
  display: flex;
  flex-direction: column;
}

这个时候,不能用 flex-basis,在 Safari 上会失去弹性,也要用 min-height。所以,最终样式大概就是:

html {
  height: 100%;
}

body {
  min-height: 100%;
  display: flex;
  flex-direction: column;
}

#nav {
  height: 4rem;
}
#bottom {
  height: 10rem;
}
#content {
  flex-grow: 1;
  min-height: 40rem;
}

另外,因为基本只有桌面浏览器需要这个功能,所以可以考虑加一个 @media (min-width: 576px) 做限制。

分类
前端

给 Markdown 里的图片增加样式

在 markdown 里添加图片很容易,用这个语法即可:

![alt](/path/to/image "title")

但是如果图片需要一些特殊样式,就不太好搞。比如前两天,老板觉得博文中的二维码太大,不好看,让我改小点。我一开始只知道可以用 HTML 来做,因为 Markdown 内建支持所有 HTML,但总觉得不够优雅,尤其是,不知道怎么要求将来写博客的人都用 HTML 来写。

于是 Google 之,找到这篇文章:How to Style Images With Markdown,写得非常好,列举了很多添加样式的方法,尤其是使用 #hash + CSS 选择器的方法,很有想象力,推荐给大家。

首先,在 Markdown 里,给图片的 URL 添加 hash。这个动作并不会造成任何实质性的影响。

![alt](/path/to/image#thumbnail)

然后,在 CSS 里,定义“src 里包含字符串 #thumbnail”的规则即可:

img[src*="#thumbnail"] {
  max-width: 10rem;
}

[attr*=val] 选择器的意思是 attr 属性里包含 val 字符串。这里用 [src$="#thumbnail"] 效果是一样的。如果你想了解所有 CSS 属性选择器,还是推荐看 MDN 文档

分类
css

诡异的 `height: intrinsic`

我厂最新也是最重要的产品 OpenResty XRay 即将开始邀请测试,所以官网上自然要添加对应的网页。目前该网页已经部署到生产环境,大家可以访问 https://openresty.com.cn/cn/xray/ 简单了解一下。

这个页面的最下面,“信任与合规”区块是一些标准化组织的认证,按照需求应该放几个 logo。然后我就很自然的用 display: flex 来做了。在桌面浏览器显示正常。

但是在 iPhone Safari 上,上面的两个图标会变得瘦长,看起来是高度计算有问题。我尝试修复这个问题,却除了写明高度,只有 height: intrinsic 可以让它显示正常。去 MDN 一搜,竟然没有这个属性?!只提到 max-contentmin-content 两种“intrinsic”的属性。

caniuse 上,可以看到 intrinsic 是个非标准化的属性,应该是以前浏览器自发实现过,后来被 max-contentmin-content 取代。但是为何 Safari 明明支持这几个属性,但是只有 height: intrinsic 能显示正常,我就不知道了,也没有查到。

先记一下吧,将来再看。如果有同学遇到类似的,图片在 display:flex 横排时尺寸出现问题,可以试试这个。

分类
前端

浏览器切换中

最近比较忙,博客有些荒废。忙过这阵子再写吧。

接下来的三周,会强迫自己把开发浏览器切到 Firefox,争取从现有项目中找到兼容性问题,并且修复。

再接下来如是反复,Chrome > Firefox > Edge > Safari > Chrome……

以后客户越来越多了,兼容性得多费心了。

下午4点更新

Firefox 不支持 SourceMap……已经切回 Chrome 了,晚上继续尝试。

8-14 更新

Firefox 团队曾经抱怨 Google Chrome 不正当竞争,故意在几个大型产品中给 Firefox 挖坑,打压他们在用户心目中的地位。然而经过我这一周来的使用,我觉得 Firefox 实在做得不够好,很难让开发者选择它作为主力开发工具。

  1. 使用 Vue Cli 会不停报错,找不到 sw.js,连接不到 sockjs-node 等等
  2. 使用 webpack 无法加载到 sourcemap,无法 debug。(应该是配置问题,因为 Vue Cli 3 可以。
  3. 报错多了就影响性能,各种卡顿。
  4. 断点不会自动跳过去,屏幕没有明显的视觉效果,开始被迫重启好几次
  5. 断点不会自动更新堆栈、变量状态,需要点击一次“Step over”

问题很多,很影响开发效率。可惜我厂老板特别爱用 Firefox,所以我还必须照顾下 Firefox,经过一周的挣扎,现在倒是好一些了,Windows 10 的表现还不错。

分类
css

使用 `position: sticky` 的要点

position 属性非常重要,它有五个可选值。“这五个选项是哪些?它们的作用如何?”是我非常喜欢的面试题。以我的经验,凡是这道题答得好的,后面多半没啥问题;这道题答不出或者错漏的,后面能翻盘的概率很低。

属性及释义大家请看 MDN,本文不再赘述。

position: sticky 比较复杂,简单来说,它包含以下特性:

  1. 当它的位置让它可以正常呈现的时候,它的定位等同于 position: static,随着正常的文档流滚动
  2. 当它的位置不足以让它正常显示,但它的父元素有足够多让它显示的空间,它的定位等同于 position: fixed
  3. 当它的父元素的空间不够让它显示,它的定位等同于 position:absolute

言说太复杂,大家可以看下这个演示,基本上就能明白了:

分类
css

使用 <wbr> 解决长 URL 的换行问题

问题

我们知道,世界上文字主要有两种:一种是以中文为代表的象形文字;另一种是以英法俄等为代表的拼音语系。前者的换行很简单,每个单字都有自己的意义,所以每个字后面都可以换行。拼音语言,字母组合本身无意义,连在一起才有意义;不同单词意义差异巨大,所以只能以单词为单位换行。

Web 开发中,屏幕宽度有限,超长文字必须换行。在 CSS 中,控制换行的属性主要有 word-breakwhite-space,其中,默认换行行为的是 word-break: normal,即以单词为单位换行。比较奇怪的是,对于 URL,我本以为类似 /.: 都是明显的单词分隔符,理应换行,但实际上,浏览器并不会在这些地方换行。如果我们使用 break-all 或者 break-word,则会使得浏览器在不合理的地方换行,如果刚好在表格里,别的列内容比较多,那么包含 URL 的单元格就会被挤压得非常窄,拉得特别高,非常难看,非常难读。

尝试

原生方法无法解决问题,只好摸索手动断行的做法。但是想完美解决问题非常困难:

第一个方案是全部换行,肯定不行;

第二个方案固定宽度换行,因为表格内容不固定,效果也很差,也不行;

老板提出了第三个方案:使用“8.3”格式,即超长字符串只保留前8个字符,后面显示“…”,然后可以手动展开。很明显,这个方案对 URL 来说没有什么价值,https:// 加起来正好 8 个字符,有意义么……即使加长也一样,因为用户有时候看域名,有时候看 pathname,也有时候看 search,我们没有办法预测。

然后老板又提出“Excel 方案”,即固定列宽,自动隐藏超出的文字,用户可以通过拖拽来调整列宽。这个方案理论上可以解决问题,但是实现难度太大,因为浏览器自带表格自适应宽度的算法,采用 “Excel 方案” 就必须放弃这个算法自己手动实现,成本很高,非万不得已也不想做。

最后,动态换行,根据表格宽度计算在哪里断行。还是不行,计算难度太大。

<wbr> 解决

这个问题困扰了我很久,直到前两天,我突然发现原来有 <wbr> 软换行的存在。而且它的兼容性非常之好,甚至连 IE8 都支持。

它的含义是“可换可不换”。当元素宽度不够需要换行,就从它这里换;如果宽度够,就不换行。所以,只需要在“可能”换行的地方加上这个元素,就可以达成我的目标。写成代码很简单,大约是这样:

function wrapUrl(url) {
  if (!url) {
    return '';
  }

  // 先把协议取出来,我不希望在协议这里换行
  const head = url.substring(0, 10);
  const left = url.substring(10);
  // 在 `?&/` 前面插入 `<wbr>`
  // 或者16个连续英文数字也要换行,打断 hash 和 md5
  return head + left.replace(/([?&\/]|([a-zA-Z0-9]{16}))/g, str => '<wbr>' + str );
}

实际效果很好,大概是这样(截图时,<wbr> 放在断开位置的后面,我觉得不好看,就调整了下):

<br> 对比,后者是固定换行,当表格内容很少,有充足的空间显示 URL 时,也会换行,就不合适了。

总结

需要注意,<table> 的渲染很特殊,浏览器要花很多时间计算每个列的内容、计算它的宽度,所以性能会比较差,这也是不要用 <table> 做布局的原因。本案例中,使用 <wbr> 实际上是想借用浏览器计算表格各列宽度的机制。所以是合适的。表格渲染之后,内容最好就固定住,不要有复杂的变动,比如隐藏/显示(前面说的8.3格式),因为内容的变化会导致浏览器重新计算布局重新渲染,比较消耗机器的性能。

以及,做了十几年前端,稍一放松,竟然有完全不清楚没用过的标签,看来有必要找时间再把 HTML、CSS 再翻一遍了。

分类
css

Stylus 实现 `content: “5”`

一般来说 Stylus 对属性是直接替换的,所以正常来说下面的 stylus

$a = 10

.foo::before
  content $a

会编译成:

.foo::before {
  content: 10;
}

这样是非法的,10 不会渲染出来。如果想让它渲染出来,必须用字符串。如果只有一个值,那就好办了,直接 $a = '10' 就好。但如果在循环里,就比较麻烦,此时,可以用 '' + 10 转换成字符串,或者使用 s(template, value),以下两种方式是等效的。

// 注意运算顺序哦
.foo
  for n in 1..5
    &:nth-child({n})::before
      $str = '' + (5 - n)
      content $str

  for n in 1..5
    &:nth-child({n})::after
      $str = 5 - n
      content s('"%s"', $str)

分类
前端

近期用 webpack-dev-server 作代理的一些经验

如今前端开发使用 webpack-dev-server 作为本地服务器已经是基本配置,加上 proxy 功能可以很好的应对 SSL、跨域、线上环境切换等需求。Vue CLI 3 里也做了相应的集成,用起来很方便。

分类
前端

在 Pug 模板中使用变量

我厂使用 Pug 作为 HTML 的预编译工具,写久了发现回不去了……Pug 写起来很高效,看久了习惯了阅读效率也很高,读了读文档,发现还有很丰富的可编程特性。于是我决定抛弃 Handlebars,以后都用 Pug 写模板了。

这次我厂官网改版,我就用 Pug 替换了 Handlebars,于是 build 脚本一下少了几十行,非常爽。其实我早就想这样做,不过卡在一个点:使用变量。

在 Pug 里可以这样使用变量:

- var name = 'meathill'

h1 hello #{ name }

以上代码将输出 <h1>hello meathill</h1>。这种用法比较简单,不过在 extends模板的时候,把变量放到哪里就不知道了,官方文档也语焉不详,我反复试了很多次无果,最终还是靠搜索找到了答案,原来要这样:

// Layout.pug
html
  block vars
    p 注意,这个 block 是重点,它出现在前面,用来注入变量
  head
    title #{ title } | My site
  body
    block content

// page.pug
extends Layout

block vars
  - var title = 'A blog'
  
block content
  h1 Blog title
  p blog content
分类
前端

关于“大漠”的一点不好的记忆

我这个人不爱交际,在行业里一直是个小透明。

不过我当年也曾想试着改变一下自己,所以找了个前端交流群加了进去。具体的群名字忘记了,总之是“大漠”创建的群。

然后发现这个群真TM恶心。群主大漠有几个小弟,一副高高在上颐指气使的样子,对群里成员吆五喝六随意攻击,不仅对发问者提出各种限制,对回答者也提出各种限制。群主也是一副老子对你们这些废柴施恩,还不快快对我歌功颂德的气势。(具体做法记不清了,好多年了。)

然后我就受不了啦。如果前端圈子都这个吊样子,那我不交际也不要紧。于是吐槽两句自己退群了。

退了之后,想说这个大漠是谁啊,怎么这么差劲,然后搜了一下,发现叫这个名字的好多,无法确定是哪个,我也懒得跑回去确认。


发生了最近的事情后,我举得按人品和气度,有可能是这个……