什么是防御式编程 ?
防御性编程是一种细致、谨慎的编程方法(习惯)。我们在写代码时常会有“以防万一”的心态,把以防万一有可能出现的情况提前考虑进去,规避以免以防万一出现带来的问题。
应用防御性编程技术,你可以侦测到可能被忽略的错误,防止可能会导致灾难性后果的“小毛病”的出现,在时间的运行过程中为你节约大量的调试时间。
比如:我们在写下面这个效果时,如果只是按设计师设计效果来开发,我们就不会考虑标题内容过长的问题。但是在实际的应用中,数据是从后台加载而来,标题的字数就有可能过长,过长之后就会导致标题溢出折行的效果如下图,带来不好的体验。
如果站在防御式编程的角度来思考,那我们就会提前把这种问题规避掉。如果标题过长,我们可以使用...省略号来处理。而不是等到项目上线,实际问题发生时,再来修改代码。
防御式 CSS 是一个片段的集合,可以帮助我们规避“以防万一”产生的问题。
我们在 CSS 布局时,是按照设计师的效果来开发的,但是实际的网页内容是动态的,网页上的内容是可以改变的,如:文字数量,图片尺寸、窗口大小等,再加上用户的一些意想不到的行为和运行环境,从而造成 CSS 布局的效果并没有按照我们预期的效果显示。
我们可以通过添加某些 CSS 代码,来避免这种情况带来的问题。防御式 CSS 是实现项目稳定性建设重要但极其容易忽视的一环。
场景一: 单行文本过长
我们设计时的理想效果是标题文字不超过 8 个字,正好显示完整。但实际应用时,有可能标题内容过长造成换行显示。我们可以添加文字溢出显示..省略号来解决。
<style> body, h3 { margin: 0; padding: 0; } .box { width: 150px; height: 150px; position: relative; margin: 40px auto; } .box h3 { height: 30px; line-height: 30px; font-weight: 100; width: 100%; background-color: rgba(0, 0, 0, 0.5); font-size: 16px; color: #fff; position: absolute; bottom: 0; text-align: center; /*以防万一,标题过长时,用...省略号显示*/ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }</style><body> <div class="box"> <img src="images/flex-06.jpg" alt="" /> <h3>"以防万一"标题过长产生的问题</h3> </div></body>
场景二:类别标签中文本过长
在这个效果中,我们并不希望标签延伸到最右侧,我们希望内容过长时,可以在一定的长度时就折行显示。我们可以通过添 max-width
属性来避免这种“以防万一”的问题。
同类的应用还有 min-width,在此就不演示了
<style> .box { width: 250px; height: 250px; position: relative; } .box span { position: absolute; background-color: rgba(119, 245, 197, 0.8); line-height: 1.3; font-size: 12px; padding: 5px 10px; border-radius: 0 50px 50px 0px; left: 0px; top: 5px; /*以防万一标签内容过长,控制最大宽度,内容过多折行显示*/ max-width: 70%; } </style> </head> <body> <div class="box"> <img src="images/ms.jpg" alt="" /> <span>植物奶油 巧克力 草莓 榴莲 花生 芝麻 小米 鸡蛋</span> </div> </body>
场景三:防止图片拉伸或挤压
我们预想的是用户按 1:1 的大小来上传头像图片,但实际用户上传的头像比例是五花八门,就会造成图片被拉伸或挤压变形。我们可以添加Object-fit: cover
来等比例裁剪图片尺寸,这样图片就不会被拉伸或压缩,不过会有一部分图片被裁剪掉。
<style> .box { width: 200px; height: 200px; border-radius: 50%; overflow: hidden; } .box img { width: 100%; height: 100%; /*保持图片原有尺寸来裁剪*/ object-fit: cover; }</style> <body> <div class="box"> <img src="images/tx2.png" alt="" /> </div> </body>
场景四:图片加载失败,文字显示问题
当图片上有文字时,如果图片加载失败,而外层容器的背景色和文字颜色接近,那么文字的展示效果就不理想;此时我们可以给图片加上对应的背景色。
这个效果大家只需做个了解就好。通常如果图片上有文字,设计师在设计效果图时都会在图片和文字中间加上一层黑色的半透明遮罩层,这样即使图片加载不出来,也不影响文字的展示效果。
<style> .box { width: 250px; height: 156px; position: relative; } .box img { width: 100%; height: 100%; object-fit: cover; /*"以防万一"图片加载失败,加上背景色,保证文字能正常显示*/ background-color: #666; } .box h3 { width: 100%; font-size: 20px; text-align: center; position: absolute; top: 40px; color: #fff; }</style><body> <div class="box"> <img src="images/rotate3.webp" alt="" /> <h3>美丽的风景图</h3> </div></body>
场景五:必要时显示滚动条
在内容比较长的情况下,可以通过设置 overflow-y 控制滚动条是否展示。但是这里更推荐将overflow-y 的值设置为 auto。如果你将 overflow-y 显式设置为 scroll 时,不管容器内容长短,滚动条都会展示出来,这种体验是不好的
<style> .box { width: 160px; padding: 20px; height: 200px; background-color: skyblue; line-height: 2; border-radius: 20px; } .box .content { padding-right: 10px; max-height: 100%; /*以防万一,用户内容不足时,不需要显示滚动条,只有内容溢出时才显示*/ overflow-y: auto; } /* 整个滚动条*/ .content::-webkit-scrollbar { width: 5px; padding: 0px 20px; } /* 滚动条轨道*/ .content::-webkit-scrollbar-track { border-radius: 10px; background-color: #000; margin: 20px 0px; } /*滚动条上的滚动滑块*/ .content::-webkit-scrollbar-thumb { width: 14px; border-radius: 10px; background-color: #ddd; }</style><body> <div class="box"> <div class="content"> 在内容比较长的情况下,可以通过设置 overflow-y控制滚动条是否展示。但是这里更推荐将 </div> </div></body>
场景六:预留滚动条空间,避免重排
当内容不足时不会出现滚动条,文字占据的宽度要宽些。当内容溢出出现滚动条时,因为滚动条要占据一部分空间,则会造成文字占据的空间变窄,因而会造成重排。我们可以元素添加scrollbar-gutter: stable;
来避免这个问题。
scrollbar-gutter 属性有三个值
没有加 scrollbar-gutter 时,未出现滚动条和出现滚动条之间的差别
加上 scrollbar-gutter:stable;后,出现滚动条和没有出现滚动,前后文字显示效果没有差异
<style> .box { width: 160px; padding: 20px; height: 200px; background-color: skyblue; line-height: 2; border-radius: 20px; } .box .content { max-height: 100%; /*以防万一,用户内容不足时,不需要显示滚动条,只有内容溢出时才显示*/ overflow-y: auto; /*预留好滚动条位置,必免引起重排*/ scrollbar-gutter: stable; } .content::-webkit-scrollbar { width: 10px; } .content::-webkit-scrollbar-track { border-radius: 10px; background-color: #000; margin: 20px 0px; } .content::-webkit-scrollbar-thumb { width: 14px; border-radius: 10px; background-color: #ddd; }</style><body> <div class="box"> <div class="content"> 当内容不足时不会出现滚动条,文字占据的宽度要宽些。当内容溢出出现滚动条时,因为滚动条要占据一部分空间,则会造成文字占据的空间变窄,因而会造成重排。 </div> </div></body>
场景七:锁定滚动链
我们会发现当子元素滚动到顶部或底部继续滚动滚轮时,会导致父元素的滚动,但这种行为有时会影响页面体验。在子元素上应用overscroll-behavior: contain
就可以禁止掉这一行为。
<style> body { height: 2000px; } .box { height: 400px; width: 200px; margin: 0px auto; overflow-y: auto; background-color: skyblue; /*当滚动到滚动条底部和顶部时,会触发父元素的滚动条滚动*/ overscroll-behavior-y: contain; }</style><body> <div class="box"> <p>1</p> <p>2</p> <p>3</p> <p>4</p> <p>5</p> <p>6</p> <p>7</p> <p>8</p> <p>9</p> <p>10</p> <p>11</p> <p>12</p> <p>13</p> <p>14</p> <p>15</p> <p>16</p> <p>17</p> <p>18</p> <p>19</p> <p>20</p> <p>21</p> <p>22</p> <p>23</p> <p>24</p> <p>25</p> <p>26</p> <p>27</p> <p>28</p> <p>29</p> <p>30</p> <p>31</p> <p>32</p> </div></body>
场景八:flex 布局中,元素使用 space-between 最后一行两边分布的问题 ?
如果我们每一行显示的个数为 n,那我们可以最后一行子项的后面加上 n-2 个 span 元素,span 元素的宽度和其它子项元素宽度一样,但不用设置高度。
为什么是添加 n-2 个 span 元素呢?
当最后一行只有 1 个子元素时,他会默认靠左,不用处理
当最后一行子元素正好时,我们就不用关心这个问题。
所以要去掉这两种情况,只需要加 n-2 个 span 元素就好
<style> .container { width: 500px; display: flex; /*弹性布局*/ justify-content: space-between; /*两端对齐*/ flex-wrap: wrap; /*超出部分换行*/ } .item { width: 120px; height: 100px; background-color: pink; margin-top: 10px; } .container span { width: 120px; /*span宽和子项宽一样*/ } </style> </head> <body> <div class="container"> <div class="item">1</div> <div class="item">2</div> <div class="item">3</div> <div class="item">4</div> <div class="item">5</div> <div class="item">6</div> <div class="item">7</div> <!--以防万一,子项个数不够,最后一排出现两端对齐,达不到预期效果--> <span></span> <span></span> </div> </body>
场景九:grid 网格中的响应式断行效果的处理
当我们想尽可能多的在一行显示子项的个数时,有可能会出现子项个数不满一行的情况。那这个时候利用网格布局,使用 auto-fill 和 auto-fit 就会是两个完全不同的效果。
auto-fill :网格的最大重复次数(正整数),如果有剩余空间,则会保留剩余空间,而不改变网格项目的宽度。
auto-fit:网格的最大重复次数(正整数),如果有剩余空间,网格项平分剩余空间来扩展自己的宽度。
以下情况只会出现在子项内容不能占满一行时。也就是说万一内容不能占满一行,则使用 auto-fill 就会出现空白问题。我们把 auto-fill 改成 auto-fit 就解决了这个问题
<style> .container { width: 100%; display: grid; /*grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));*/ /*以防万一,子项不足占据一行时*/ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); grid-template-rows: 250px; grid-auto-flow: row; grid-auto-rows: 250px; gap: 10px; } .container .item:nth-child(even) { background-color: skyblue; } .container .item:nth-child(odd) { background-color: pink; }</style><body> <div class="container"> <div class="item"></div> <div class="item"></div> <div class="item"></div> <div class="item"></div> </div></body>