掌握面试——弹出框的实现

这道面试题,当初我面试的时候被问过两次,因此比较深,此外,我记得还有设计模式的考察,所以,有深刻的体会。

面试题主要考察什么

面试不是个轻松的活,不管是对面试官还是面试者都一样。对于面试官来说,别的先不管,首先一点技术要过关,对候选人的基本要求就是基础扎实,有相关项目经验,有解决问题的能力,思路清晰,易于沟通。而对于面试者来说要技术扎实,知识面要广,要有技术闪光点,对于各种技术提起来都知道点,但经不起深层次的询问,这种‘一瓶子水不满,半瓶子水晃地’现象很不受面试官的喜欢。

虽然,我工作的时间不长,但被面试过,也面试过别人。所以,将这道题被问的情况和自己的体会理解分享给大家。

注:面试公司 去哪儿,360

用 html + css 实现一个弹出框

注:这是按照面试官的预想可能出现的情况设定的,不同的面试者临场发挥不一样,面试官可能问的问题也有所变化,但归根结底,这个系列的问题大致如下。

这里你在回答前需要问清楚实现有什么限制没,如果没有,你可以以任意的方式来实现;如果有,问清楚限制,一般限制有如下情况,一种定宽定高,另一种不定宽高(补丁宽高一般用js控制),但是使用css3亦可实现,如果你回答出一种,面试官往往会问有没有其他的实现方法,如果面试管这样问了,你如果,回答没有,会被扣分的。面试会进入下一个环节。

弹出框垂直水平居中

html:

<div class="box-default box-wh box-vc-mar">this is a pop-up box</div>

css:
.box-default {
position: fixed;
top: 50%;
left: 50%;
z-index: 99;
padding: 20px;
background-color: white;
border: 1px solid #ccc;
border-radius: 8px;
}

.box-wh {
    width: 200px;
    height: 200px;
}

.box-vc-mar {
    margin-left: -100px;
    margin-top: -100px;
}

JSFiddle效果演示

如果弹出窗的宽高不定

html:

<div class="box-default">this is a pop-up box</div>

css:

.box-default {
    position: fixed;
    top: 50%;
    left: 50%;
    z-index: 99;
    padding: 20px;
    background-color: white;
    border: 1px solid #ccc;
    border-radius: 8px;
}

.box-vc-mar {
    margin-left: -100px;
    margin-top: -100px;
}

js:

var $box = $('.box-default'),
    bw = $box.width(),
    bh = $box.height();
$box.css({
  marginLeft: - bw/2 + 'px',
  marginTop: - bh/2 + 'px',
});

JSFiddle效果演示

有没有其他方式实现不定宽高的弹出窗

答案:有

html:

<div class="box-default box-vc-trf">this is a pop-up box that only html+css</div>

css:

.box-default {
    position: fixed;
    top: 50%;
    left: 50%;
    z-index: 99;
    padding: 20px;
    background-color: white;
    border: 1px solid #ccc;
    border-radius: 8px;
}

.box-vc-trf {
    -webkit-transform: translate(-50%, -50%);
    -ms-transform: translate(-50%, -50%);
    -o-transform: translate(-50%, -50%);
    transform: translate(-50%, -50%);
}

JSFiddle效果演示

4.如果给弹出窗加一个遮罩层该如何实现

html:

<div class="box-default box-vc-trf">this is a pop-up box that only html+css</div>
<div class="mask"></div>

css:

.box-default {
  position: fixed;
  top: 50%;
  left: 50%;
  z-index: 99;
  padding: 20px;
  background-color: white;
  border: 1px solid #ccc;
  border-radius: 8px;
}
.box-vc-trf {
  -webkit-transform: translate(-50%, -50%);
  -ms-transform: translate(-50%, -50%);
  -o-transform: translate(-50%, -50%);
  transform: translate(-50%, -50%);
}
.mask {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 98;
  background-color: #000;
  opacity: 0.75;
  filter: alpha(opacity=75);
}

JSFiddle效果演示

下面的问题就问的比较深了,牵涉到了一些简单业务实现

触发按钮实现弹窗

根据是之前实现的结果,如果在页面中有一个按钮,通过触发钮如何实现弹出窗以及实现关闭弹出窗

示例代码如下:

html:

<div class="box-default box-vc-trf">this is a pop-up box that only html+css</div>
<div class="mask"></div>

<button id="btn">click it</button>

css:

.box-default {
  position: fixed;
  top: 50%;
  left: 50%;
  z-index: 99;
  display: none;
  padding: 20px;
  background-color: white;
  border: 1px solid #ccc;
  border-radius: 8px;
}

.box-in {
  display: block;
}

.box-vc-trf {
  -webkit-transform: translate(-50%, -50%);
  -ms-transform: translate(-50%, -50%);
  -o-transform: translate(-50%, -50%);
  transform: translate(-50%, -50%);
}

.mask {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 98;
  display: none;
  background-color: #000;
  opacity: 0.75;
  filter: alpha(opacity=75);
}

.mask-in {
  display: block;
}

js:

var $box = $('.box-default'),
    $mask = $('.mask'),
    $btn = $('#btn');

$btn.on('click', function(event) {
  event.preventDefault();
  $box.addClass('box-in');
  $mask.addClass('mask-in');
});

$mask.on('click', function(event) {
  event.preventDefault();
  $(this).removeClass('mask-in');
  $box.removeClass('box-in');
});

JSFiddle效果演示

这个只是我的实现,每个人的实现都会有所不同。

此外,像这里如何关闭弹出窗的实现方式,并不一定要通过触发遮罩层来实现,我们常见的实现是在弹出窗中加一个关闭按钮。像在这里这种模棱两可的地方,往往存在陷阱,我建议在你给出答案前先问清楚这个关闭弹出窗要如何实现,并给出自己的方案,因为在实际的项目开发的过程有些细节问题产品经理并不会面面俱到,会有疏漏的地方,而作为一个前端你要及时的提出来,进行沟通确认,这里可能会考察你的观察,判断和沟通能力。但有些时候你问过后,面试官会对你笑笑,让你自由发挥,你这时就会明白,这是一处坑。但有时候不是这样,所以,面试时要主动与面试官沟通,避免因细节问题在面试过程中被降分。

如果在页面中有多个按钮,那么这个弹出窗要如何实现

同上,根据之前的建议在回答问题之前要问清楚问题中模棱两可的地方。

1.多个按钮是不是成百上千个,或者就是几个。

2.是否触发不同按钮弹出的窗口现实的内容不同。

同时也可以不问,你只要明白要考察的知识点即可,问只是让你更清楚的知晓面试官的考察点。

html:

<div class="box-default box-vc-trf">this is a pop-up box that only html+css</div>
<div class="mask"></div>

<button class="btn">click</button>
<button class="btn">click2</button>
<button class="btn">click3</button>
<button class="btn">click4</button>
<button class="btn">click5</button>
<button class="btn">click6</button>

css:

css 同上个示例

js:

$(function(){
    var $box = $('.box-default'),
        $mask = $('.mask');
    
    $mask.on('click', function(event) {
      event.preventDefault();
      hideMask();
    });

    $('.btns').forEach(function(el, i) {
        el.on('click', function(event) {
          event.preventDefault();
          var content = getText($(this));
          showMask(content);
        }
    });

    function getText(obj) {
        return obj.text()
    }

    function showMask(content) {
      $box.addClass('box-in').html(content);
      $mask.addClass('mask-in');
    }

    function hideMask(obj) {
      $mask.removeClass('mask-in');
      $box.removeClass('box-in');
    }
});

这里主要考察了两点:

1.js基础是否扎实

  • 表现和行为的分离
  • 可维护性
  • 可扩展性

2.是否使用了事件代理

代码修改如下:

$(function(){
    ...

    $(document.body).on('click', '.btn', function(event) {
      event.preventDefault();
      var content = getText($(this));
      showMask(content);
    });

    ...
});

JSFiddle效果演示

有时候面试官会让你用原生js来实现事件代理,那么我们应该如何回答呢?

由于事件委托可以实现目标对象的隐藏,在开发这对于我们保护一些核心的对象非常有用,不过实话来说,在JavaScript中就是call,apply的使用,因为js中只有这两个方法提供了改变当前函数内部this作用域的功能。当然这只是对象的委托,而要实现对类的委托则要相对复杂些,对类的实现大家有兴趣的可以参考以下Prototype.js或Jquery中的相应实现。

事件代理就要改变侦听器的位置,或者说改变事件绑定的对象。得益于js的事件传播机制,实现起来非常容易。

下面这个例子大家都可能见过:

html:

<ul id="nav">
    <li><a href="http://blog.csdn.net/weixin_41559723?ref=toolbar/">CSDN</a></li>
    <li><a href="https://segmentfault.com/u/birenyangguangcanlan">Segmentdefault</a></li>
    <li><a href="https://github.com/lvzhenbang">github</a></li>
    <li><a href="https://juejin.im/user/58b83c66128fe100642f5297/">稀土掘金</a></li>
    <li><a href="https://home.cnblogs.com/u/1309794/">博客园</a></li>
</ul>

js:

window.onload = function(){
  var nav = document.getElementById("nav");
  nav.onclick = function () {
    var e = arguments[0] || window.event,
        target = e.srcElement ? e.srcElement : e.target;
    if (target.nodeName.toLowerCase() === 'a')
        alert(target.innerHTML);
    return false;
  }
}

JSFiddle效果演示

var addEvent = (function () {
    if (document.addEventListener) {
      return function (el, type, fn) {
        el.addEventListener(type, fn, false);
      };
    } else if (document.attachEvent) {
      return function (el, type, fn) {
        el.attachEvent('on' + type, fn);
      }
    } else {
        el['on' + type] = fn
    }
  })();

上面这个事件监听的兼容性代码片段,是面试官问我是否对jQuery源码有所了解,我当然说有,就让我说一下如何实现。

好了,关于事件代理的问题就不深入讨论了,我们接着了解关于弹出窗的其他问题。

遮罩层的共用问题

同一个页面我们可以考虑,直接在页面中加一个表示遮罩层的div,
但是对于不同的页面我们最好用js来控制。

问题:这里面试管会问,如何实现遮罩层的共享?

我们通常会写下面的一个代码片段:

function createMask() {
    return $(document.body).append($('<div>').addClass('mask'));
}

虽然,这样添加一个遮罩层,在隐藏的时候可以使用remove()移除它,但显然这样在页面中频繁的添加,删除dom元素不合理。

问题:有没有什么方法可以对它进行优化?

我们可能会考虑对它进行优化,先在全局创建一个这个遮罩层div,用一个变量来引用它。代码如下:

var mask = $(document.body).append($('<div>').addClass('mask'));

这样页面就只会创建一个遮罩层div,但是可能出现,我们在使用的过程中不会用到这个div,这样我们就会造成资源的浪费,dom的节点就会平白多出一个无用的div。

这时我们可以借助一个变量来判断是否创建过div。代码如下:

var mask;
function createMask() {
    if (mask) {
        return mask;
    } else {
        mask = $('<div/>').addClass('mask').appendTo($(document.body));
        return mask;
    }
}

细心的你是否发现这是一个单例?

当你在面试前对这个问题有研究,你说用一个单例模式来解决,面试官会让你先实现,然后让你说说单例模式的理解,最后询问相关的问题。如:什么是单例模式,单例模式有何优缺点,如何使用单例模式。

但是也有时候面试官会让你直接使用一个设计模式来对它进行优化?

在这里,面试官的询问方向跟面试官的知识面和掌控度有关。所以,面试大厂或者面试中/高级工程师的童鞋还是把自己的所学知识技能系统化比较好。

你发现上面那段代码实现有哪里不妥吗?对了,你在函数体内改变了函数外的变量mask的引用,在多人协作的项目中,你写的createMask是一个不安全的函数,此外我们开发中应尽量避免像mask这样的全局变量使用,用一个局部变量如何来解决这个问题呢,我想很多同学会想到闭包,修改后的代码如下:

function createMask() {
    var mask;
    return function() {
        if (mask) {
            return mask;
        } else {
            mask = $('<div/>').addClass('mask').appendTo($(document.body));
            return mask;
        }
    }           
}

好吧,到了这里你可能在心里想‘这下总算完了吧’,是的,我想说的是这道题你答成这个样子,对于中级工程师来说已经过关了,但是对于高级工程师来说,你还需要对这个代码进行优化,如果经常研究源码的童鞋会见到这样写的代码片段:

function createMask() {
    var mask;
    return function() {
        return mask || mask = $('<div/>').addClass('mask').appendTo($(document.body));  
    }           
}

对于那些开发框架,常用库或插件的大牛来说,同样的功能,同样的性能,不会多浪费一个字符。

如果是面试高级工程师,可能还会被问到,如何多个功能都要用到单例模式,该如何解决?

function singleton(fn) {
    var res;
    return function() {
        return res || (res = fn.apply(this, arguments))
    }
}

var createMask = singleton(function () {
    return $('<div/>').addClass('mask').appendTo($(document.body));
});

其实,这里又用到了另一种设计模式——桥接模式。

面试到这里一般就算完了。通过上面的代码我们发现,设计模式也不是什么洪水猛兽,只不过是设计模式的使用灵活多变,但要想真正完全掌握设计模式,不是看两篇文章就行的最重要的还是要多想,多实践。

更多面试问题请关注我的github

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,830评论 25 707
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,727评论 2 17
  • transform 功能:向元素应用2D或3D转换语法:transform:none|transform-func...
    别翘二郎腿阅读 240评论 0 0
  • 昨天和妈妈和他一起去了中卫的寺口子。在上山下山的路途中有了一丝丝的想法。 上山 上山的路途比较艰难 有一段特别陡的...
    暖阳洋的生活日记阅读 283评论 0 1
  • clear all 清除所有变量 close all 关闭所有画图 clc 清屏 文件开头上来先 clc clea...
    夕阳下的不回头阅读 223评论 0 0