『布局』之『弹窗』

本文是对 window.jade 相关源码的补充说明,请对照源码、尝试修改,以帮助理解。

代码

Jade 代码:

html 
    head
        title Modal Window
        link(href="/window.css", rel="stylesheet", type="text/css")
        script(src="/window.js", type="text/javascript")
    body
        div.content_container
            div.content
                span I am content here!
                span here make the content long long long long long long ...
                a(href="#", onclick="open_window()") click me to open window.
        
        div#my_window.modal_window_container
            div#my_real_window.modal_window
                div.window_bar
                    a.close_button(href="#", onclick="close_window()")
                div.window_body
                    p I am content in window.

我们温故知新一下,div#my_window.modal_window_container 这个 Jade 的语法,表示一个 DIV,id 是 my_window, class 是 modal_window_container
我们在这个基础上增加一个 class,比如 #my_window.modal_window_container.new_class,(此处没有使用 div 开头,因为 Jade 默认的 DOM 类型就是 DIV),最终得到的 HTML 代码如下:

<div id='my_window' class='modal_window_container new_class'> </div>

对应的 SCSS 样式代码:

.modal_window_container{
    position: absolute;
    top: 0; left: 0;  
    width: 100%; height: 100%;
    background: rgba(0, 0, 0, 0.75);
}
.modal_window{
    position: absolute;
    left: 60px; top: 50px;
    width: 500px; height: 350px;
    border-radius: 10px;
    background: rgba(255, 255, 255, 0.8);
    .window_bar{
        position: relative;
        height: 30px;
        background: red;
        border-top-left-radius: 10px;
        border-top-right-radius: 10px;
        .close_button{
            font-family: sans-serif;
            text-decoration: none;
            position: absolute;
            right: 5px; top: 0px;
            width: 30px; height: 30px;
            line-height: 30px;
            color: #fff;
            &:before{
                content: "x";  margin-left: 10px;
            }
            &:hover{ color: black; }
        }
    }
}

实际效果截图:

引入 Javascript

var open_window = function(){
    // 显示窗口
    var my_window = document.getElementById('my_window');
    my_window.style.display = 'block';

    // 计算坐标,保持居中
    var my_real_window = document.getElementById('my_real_window');
    var page_width = document.documentElement.clientWidth;
    var page_height = document.documentElement.clientHeight;
    my_real_window.style.left = (page_width - my_real_window.clientWidth)/2 + 'px';
    my_real_window.style.top = (page_height - my_real_window.clientHeight)/2 + 'px';
}

var close_window = function(){
    var my_window = document.getElementById('my_window');
    my_window.style.display = 'none';
}

我们在最初介绍 Javascript 的时候,说它是可以改变 HTML 与 CSS 的。一般 Javascript 上的基础逻辑:

  1. 找到 Dom 元素,比如 document.getElementById (这是一个函数)
  2. 获得 Dom 元素的属性,比如 document.documentElement.clientWidth
  3. 改变 Dom 元素的属性,比如 my_real_window.style.left = ? 的赋值

对了,(page_width - my_real_window.clientWidth)/2 + 'px' 中,其实是 整数+字符串,在其它编程语言中,这两种不同类型一般是无法相加的;但这个也呈现了 Javascript 『不拘小节』的特性。
虽然与 HTML、CSS 相比,Javascript 的难度上了不止一个等级;但因为这种『不拘小节』,也说明它最初来到世界时,本身是不严谨的,换句话说,它的上手难度其实也不会太高。
只要自己想要达到什么效果,而 HTML+CSS 无法满足的时候,就可以尝试使用 Javascript,在具体解决问题的过程中,会很自然地学会基础的 Javascript。

HTML+CSS 的复用,是通过 CSS 的样式规则来实现的,大部分就是 CSS 中 (class) 的概念。
而 Javascript 的复用是一个 .js 文件、库、函数、类,这也是大部分编程语言的复用模式。
Javascript 中 (class) 的概念,或者说几乎所有(后端)编程语言,都有 的概念,这个与 CSS 中 的概念是截然不同的,这要分清楚。`

在本段中,我们使用的是原生的 Javascript,在实际项目中,我们一般会引入 jQuery 这个基础框架,从而提高代码的可读性,以及写代码的效率。
说到 Javascript 的框架,或许未来你会面对 Angular、React、Vue,但它们与 jQuery 相比,更倾向于 App 方向;而 jQuery 可以单纯理解为更容易书写的 Javascript。
现在大部分 Javascript 的框架,会和 Node.js 关联在一起。Node.js后端语言,跑在服务器上,用的语法跟 Javascript 一样;而 HTML、CSS、Javascript 是前端语言,跑在浏览器上的。
简而言之,在入门阶段,Javascript 的 框架,用 jQuery 就好;其它的,未来有需要,一边用一边学就好了。

注意: 一般实际项目中,如非必要不会自己手工写弹窗的机制,而只是处理弹窗的内容布局,弹窗本身的机制会引入对应的 Javascript 库。简言之,本 Demo 的意义,就是在于 Demo,而非实际应用场景。

再调整

有一个问题,怎么默认不让弹窗显示,只有点击某个触发的链接才打开弹窗呢?
很简单呀,.modal_window_container CSS 设个默认属性为 display: none 就可以了。

引入 Javascript 后,我们可以关闭弹窗、触发弹窗了,弹窗的窗口也居中了 (如下图):


一些代码中涉及到的技巧、经验

保持单行垂直居中

height == line-height 的时候,就可以了。

height: 30px;
line-height: 30px;

如果是多行的垂直居中,会比较麻烦,因为一个元素在使用 CSS 样式渲染的时候,其实并不知道自己当前当前的高度,这个高度往往是由内部所有的子元素+当前元素属性决定的。高度未知,就无法做到垂直方向的居中;当然,此时可以使用 Javascript 了。
但是,在本课程最开始的 Level 1 里,我们已经介绍过垂直居中的办法了,现在再回过头去看看,或许也能更容易理解一些。

超级链接也是按钮

比如 Demo 中弹窗的关闭按钮,其实就是一个 A 元素(超级链接),只是点击它的时候,打开的并不是网页,而是调用了一个 Javascript 的函数。
同样,我们在 Level 1 最开始的时候,已经介绍过 函数 是什么,如果 HTML+CSS 构建的最终页面,需要交互,基本上都会引入 Javascript,也经常可以放在一个 A 元素上面,点击从而触发事件。
因为 A 元素本身有兼容性更广的伪类支持,比如 a:hover,表示鼠标悬浮其上时候的状态。

兼容性更广是什么意思?
在更早版本的浏览器,特别是 IE 浏览器,非 A 元素的 :hover 这个伪类是不生效的……

另外一方面,由于普通的页面,基本上都是引入了 Javascript,而多数的 Javascript 基础库会对应浏览器的一些兼容问题,:hover 这个伪类的触发,通过 Javascript 的机制也能实现。
于此同时,我们应该要说明下: 如非必要,不引入 Javascript 的基本原则。但也不用太在乎这个原则,权作为一种提醒;因为,真需要用到 Javascript 的时候,也就是说明它是必要的。

选择器、伪类、伪元素

:hover:first-child:before 这些都是 CSS 的选择器,与此同时,:hover 是一个伪类,而 :before 是一个 伪元素
所谓的 ,在 HTML+CSS 的概念中,就是 HTML 中的 class,一般对应到 CSS 中以 . 开头的规则名。
所谓的伪类,就是说,它是一个 class,但并不是常见的以 . 开头的。
所谓的 伪元素,我们在前面其实已经接触到好几次了,除了 :before 之外还有 :after,它们的意思是,首先是一个 DOM元素,其次,它们实际没有出现在 HTML 的源码中,是由 CSS 规则构造出来的。

换个角度来看,知道这些术语的存在即可,不深究。
比如 :hover 肯定会用起来的,但是不知道它叫 伪类,又有什么关系?
如果 :before 用起来了,知道它怎么使用,同样也不知道叫 伪元素,肯定也没有关系!
另外呢,伪元素就算不会用,直接在 HTML 的结构中通过其它真实的元素,完成视觉修饰的作用,也肯定没有关系!

实用主义出发,我们追求用起来就已经足够了。即使用得不熟练,能知道怎么用(可能还需要通过搜索引擎获取帮助),其实是不会影响最终界面的实现。
但是,从纯技术的角度出发,假设你不知道这些基本技术术语,是要出问题的。
自己把握这个度,就可以了。