<ul id="list" class="foo">
  <li>#0</li>
  <li><span>#1</span></li>
  <li>#2</li>
  <li>#3</li>
  <li><ul><li>#4</li></ul></li>
  ...
  <li><a href="//v2ex.com">#99998</a></li>
  <li>#99999</li>
  <li>#100000</li>
</ul>
<ul> 添加一个类 bar<li><li> 后面增加一个 <li> , 其文字内容为 <v2ex.com /><li> 弹窗显示其为当前列表中的第几项。最近又来了个资深工程师面试,结果现场写代码环节写不出上面类似的题目。🌚
讲道理这题真的不难啊,就是简单的 DOM 操作,没有任何奇技淫巧,现场写:一台 MBP / 30 分钟 / 允许 Google ,只要基本功够扎实应该都能写出来吧。
其实现场考的版本比这个还简单,这个是为了发帖稍微整理后的版本,大家来喷一波。
(顺便补个广告,招前端,薪资对标阿里 P6 ,可年后入职,年前入职可以补偿年终奖,因为不是招聘结点我就不放邮箱了,有兴趣私我哈)
|  |      1sagaxu      2016-12-20 18:21:05 +08:00 可以用 jquery 吗?可以的话,我这个后端也会啊 | 
|  |      2weegc      2016-12-20 18:24:05 +08:00 感觉可以试试,工作地点在哪里? | 
|  |      3ByZHkc3      2016-12-20 18:27:11 +08:00 via Android 还好,考察了不少基础知识点。谷歌都做不出来的人不招也罢~ 但是招人不得贴待遇吗 | 
|      4v1024      2016-12-20 18:30:26 +08:00 via iPhone 能做出来.. 搭车问下 p6 啥概念..? | 
|      5heeexy      2016-12-20 18:31:08 +08:00  8 这道题想必考察的是会不会用 MBP... | 
|  |      6reus      2016-12-20 18:38:37 +08:00 能 google 都做不出来,水平也太差了…… 工作地点哪里? | 
|      7sudoz      2016-12-20 18:41:31 +08:00  1 我就是那个坐在电脑前半小时没憋出来的资深前端……哈哈 | 
|  |      8forgcode      2016-12-20 18:45:26 +08:00 阿里 P6 是啥级别!这个面试题是什么级别! | 
|  |      9RE      2016-12-20 18:47:33 +08:00 同问工作地点在哪 | 
|  |      10leega0      2016-12-20 18:47:42 +08:00 via iPhone 我怎么没遇到可以用 Google 的面试 | 
|  |      12xcodebuild      2016-12-20 18:57:40 +08:00  1 允许 Google 都写不出来不可能吧。。 | 
|  |      13ByZHkc3      2016-12-20 18:59:50 +08:00 via Android @codefalling 只能说面试者差劲啊,哪怕基础不好,会 Google 都能写出个大概 | 
|      14helloccav      2016-12-20 19:02:29 +08:00 允许 Google 还写不出? 弱问一下,允许加载 jquery 吗?我不懂原生 js ,逃^^^^ | 
|  |      15ClassicOldSong      2016-12-20 19:08:59 +08:00 这题有什么难度。。。。。 不过要说奇技淫巧的话还是有的,就看你想不想用了 | 
|      17ZhaoMiing      2016-12-20 19:09:31 +08:00 老题啊, DOM 操作和闭包 | 
|      18bramblex      2016-12-20 19:23:29 +08:00 操作 DOM 那么脏的事情不干 | 
|  |      19Septembers      2016-12-20 19:34:49 +08:00  1 document.querySelector('#list').classList.add('bar') document.querySelector('#list:nth-of-type(10n)').remove() document.querySelector('#list:nth-of-type(500n)').appendChild(() => { const element = document.createElement('li') element.innerText = '<v2ex.com />' return element }) document.querySelector('#list').addEventListener('click', event => { const target = event.target alert(Array.from(target.parentElement.childNodes).findIndex(ele => ele === target)) }) | 
|  |      20ljcarsenal      2016-12-20 19:37:15 +08:00 @v1024 25w+ 的吧 | 
|      21MrFireAwayH      2016-12-20 19:37:44 +08:00 via Android @bramblex 活捉我家 Kevin 酱~ | 
|  |      22ljcarsenal      2016-12-20 19:40:42 +08:00 刚想看看是哪个公司 发现有前同事在你厂 | 
|  |      23lijsh      2016-12-20 19:59:35 +08:00 | 
|  |      24lijsh      2016-12-20 20:00:10 +08:00 上面的题目是: js 中如何获得 <ul> 下的第一个 <li> | 
|      25dxcqcv      2016-12-20 20:45:56 +08:00 第 3 道挺难的,我也超过时间了,果然还是技术不行 贴一波答案 ```javascript var u = document.querySelector('.foo'); u.classList.add('bar'); var li = u.querySelectorAll('li'); console.log(li.length); li[9].parentNode.removeChild(li[9]); var t = '<v2ex.com />'; li[499].insertAdjacentHTML('afterend','<li>'+t+'</li>'); for(var i =0, l = li.length; i < l; i++) { (function(i){ li[i].onclick =function(){ alert(i) } ; }(i)) } ``` 最后贴个简历,(简历地址)[https://dxcqcv.github.io/resume/html/index.html], 楼主招不招中级前端,哈哈 | 
|  |      26otakustay      2016-12-20 20:46:41 +08:00 这题就是个应届生级别,有 querySelector 直接上 nth-child 没有就 for 一把的事情…… | 
|      27kisnows      2016-12-20 20:50:00 +08:00 P6 对应的是级别是 资深 呀。 那 P5 对应的是 高级 ? | 
|  |      2839Sc06lk7Khhc4qV      2016-12-20 20:55:02 +08:00 校招题目都比这难了。。。😂 | 
|  |      29alvie      2016-12-20 21:22:21 +08:00 作为一个后端工程师表示都会做... | 
|  |      30wly19960911      2016-12-20 22:10:58 +08:00 via Android 这难度几乎没有吧,考察了几个 dom 操作,上面说会超过时间我明天看看,不知道会不会超过时间,毕竟标签这么多,原来做过一个很垃圾的代码整个运行效率都被脱累过。。 | 
|  |      32murmur      2016-12-20 23:53:43 +08:00 考点难道是 IE8/9 下不能直接删除元素必须先找到元素然后用父节点删子节点? | 
|      33oglop      2016-12-21 00:57:10 +08:00 via Android 我觉得这道题的内涵在于测试面试者如何自举翻 wall 上 google | 
|  |      34wdhwg001      2016-12-21 02:12:18 +08:00 via iPhone 内啥… V2EX 没有私信功能… | 
|  |      37skydiver      2016-12-21 02:37:45 +08:00 via Android 这题目我一个后端都会… | 
|  |      38emric      2016-12-21 04:35:45 +08:00 题目不错, ie9+ 还算简单。 1. classList 2, 3. list.children or querySelector + css 4. 事件委托 + indexOf + 阻止冒泡 | 
|      39DWabo      2016-12-21 04:42:35 +08:00 via Android 我一个学了 2 个月的初级前端也能做出来。 | 
|  |      40justyy      2016-12-21 05:17:43 +08:00 可以 google 根本就不难。我记不住,但是我会用 google, 三两下就能找到答案。。 问题是 国内面试的时候 能上 google 么? | 
|  |      41xcv58      2016-12-21 05:36:11 +08:00 竟然直接操作 DOM ,这公司我不去。 | 
|  |      42des      2016-12-21 08:21:40 +08:00 via iPhone 这也难?你们都不是前端吧? 说监听事件多的什么情况?难道你们不知道事件冒泡机制?? 还有说闭包的,难道你们没考虑插入和删除会改变排序? 只不过计算是第几项,没有什么好办法,为了快速解决这个题目,我会选择遍历。 就这题目还能谷歌,你们要求也太低了吧? 如果换我,我也会觉得是在考验我使用 MBP 的能力,因为我也不会用, lol | 
|  |      43coolair      2016-12-21 08:25:58 +08:00 via Android 有 google 可以写出任何代码,只要自己有思路 | 
|  |      44halden      2016-12-21 08:26:17 +08:00 招前端考原始 js 意义不大吧,现在做什么不都是用第三方库的 | 
|  |      45sxd      2016-12-21 08:27:50 +08:00 饿了么还考操作 dom 啊 不都用 Element 么 (手动滑稽 | 
|  |      47Ahri      2016-12-21 08:41:43 +08:00 可以 google 应该能做出来,不过这问题跟茴香豆的茴字有几种写法差不多,无用的知识。 | 
|  |      48Felldeadbird      2016-12-21 09:14:48 +08:00 这道题很简单啊,我这个后端看一眼就知道了。 当然,我是说用 JQ 的情况下。 23333 。不过用原生 JS 写,也没啥压力啊。面试过程如果时间不够,就不考虑兼容呗。 | 
|  |      49ybh37      2016-12-21 09:19:30 +08:00 百度就能搞定,还用 Google | 
|  |      50Mcatt      2016-12-21 09:36:27 +08:00 好简单喔 | 
|      51haocity      2016-12-21 09:49:43 +08:00 var ul=document.getElementById('list'); ul.className+=' foo'; var li=ul.getElementsByTagName('li'); ul.removeChild(li[9]); li[499].insertAdjacentHTML('afterend', '<li>v2ex.com </>'); for(var i =0, l = li.length; i < l; i++) { x(i)} function x(i){ li[i].onclick =function(){alert(i)} } 应该能兼容 IE6 ..还没实验 | 
|  |      52ajan      2016-12-21 09:52:21 +08:00 这题这么简单,还有 Mac 答题机器,答不上来对不住自己。 BTW ,我前些时间面试碰到有家鸟公司给了三页笔试题,题多得连答题的空间都没,在纸上写代码,真是日了狗了,记得有道题是 CSS 绘制他们公司的 logo, 很简单,但是连稿子或答题空间都不留,这垃圾公司,我坐了 2 分钟毅然离开。 | 
|      55SilentDepth      2016-12-21 10:09:46 +08:00 @ajan CSS 画 logo 确定不是他们美工塞的题? 2333 | 
|      56SilentDepth      2016-12-21 10:12:13 +08:00 @lynth 如果只包含根级<ul>的<li>, children 就好了;如果要包含所有可能的<li>, querySelectorAll('li')。我觉得是后者 | 
|  |      57Nutlee      2016-12-21 10:13:30 +08:00 遍历操作 li ,同时把 li 索引缓存到自己身上,用事件委托绑定事件。。  是我想简单了么 ?? | 
|      59tomwei7      2016-12-21 10:15:03 +08:00 我觉得我这个后端都能写出来,当然要靠 chrome console 强大的代码不全功能 | 
|      60funnuy      2016-12-21 10:21:51 +08:00 用 NodeIterator 写了一个 | 
|  |      61wungqiang      2016-12-21 10:24:48 +08:00  1 考察点: - 会考虑到特性检测吗 if ('querySelectorAll' in xxx) {} - 选择器会用 id selector 还是 class selector - 闭包实现,深入问其它方法 - DOM 基本操作 - 性能及其它 | 
|  |      62hanyang      2016-12-21 10:30:32 +08:00 不管用什么库 这些操作 DOM 的基本知识都是要掌握吧 | 
|  |      63homfen      2016-12-21 10:31:48 +08:00 贵公司的资深工程师要求好低 | 
|  |      65henryxie2093      2016-12-21 10:49:36 +08:00 帖子标题本来就说明白了,为什么大家还要说这题没难度 | 
|  |      66chemzqm      2016-12-21 10:55:03 +08:00 @Septembers 最后写错了, e.target 会找到 li 下的 span | 
|      68haocity      2016-12-21 11:34:08 +08:00 @lynth  额 审题不严 当时在上别的课 就看了一遍题目 记不清了 写了直接发上来来了 ul.className+=' foo';这里也写错了 应该是加上 bar ul.className+=' bar'; li 改成这样方法来取 var li=[]; for (var i = ul.childNodes.length - 1; i >= 0; i--) { if(ul.childNodes[i].nodeName=='LI') { li.push(ul.childNodes[i]) } } | 
|  |      69hoosin      2016-12-21 11:36:48 +08:00 居然没有一个人封装一下  ```js var $ = document.querySelector.bind(document) ``` | 
|  |      70ZoomZhao      2016-12-21 11:41:50 +08:00 | 
|      71echol      2016-12-21 11:43:36 +08:00 不是很懂 (function(){ const rootNode = document.querySelector('#list'); /*#1*/ rootNode.classList.add('bar'); rootNode.querySelectorAll('ul').classList.add('bar'); /*#2*/ const delindex = 10, addindex = 500; const liNodes = rootNode.querySelectorAll('li'); rootNode.removeChild(liNodes[delindex - 1]); /*#3*/ const escapeHtml = function(string) { var entityMap = { "&": "&", "<": "<", ">": ">", '"': '"', "'": ''', "/": '/' }; return String(string).replace(/[&<>"'\/]/g, function (s) { return entityMap[s]; }); } const addnode = document.createElement('li'); addnode.innerHTML = escapeHtml('<v2ex.com />'); if(liNodes[addindex -1].nextElementSibling == null){ liNodes[addindex -1].parentElement.appendChild(addnode); } else{ liNodes[addindex -1].nextElementSibling.insertBefore(addnode); } /*#4*/ for(let i = 0, max = liNodes.length; i < max; i++){ liNodes[i].setAttribute('data-index', i); } rootNode.addEventListener('click', (e) => { if(e.target.nodeName !== 'LI') return; alert(e.target.getAttribute('data-index')); }, false); }) | 
|  |      73Septembers      2016-12-21 12:13:48 +08:00 @chemzqm event.target.closest('li') 就对啦 不过没有考虑深度嵌套 | 
|  |      74Septembers      2016-12-21 12:16:08 +08:00 | 
|  |      76Septembers      2016-12-21 12:17:55 +08:00 @ZoomZhao see http://mdn.io/closest @echol escapeHtml 用不着这样 textContent 就好啦 see http://mdn.io/textContent | 
|      77ahkxhyl      2016-12-21 12:21:06 +08:00 jquery 可以搞搞 纯 js 查资料可以~~ 非前端人员 打酱油后端 php 路过~~ | 
|  |      78exoticknight      2016-12-21 12:52:35 +08:00 后悔没去饿了么校招…… 1. classList.add 或者 className 2. children[9] 或者 nth-of-type(10), removeChild 3. insertAdjacentElement('afterend', '<li><v2ex.com /></>'),不想转码就老老实实 createElement + innerText / textContent ,再 insertAdjacentElement 4. 事件委托大家都会了……重点代码就是 [].slice.call(ul.children).indexOf(e.target),不过 indexOf 可能有兼容问题,换 for 循环也可以 | 
|  |      79Anshi      2016-12-21 13:08:10 +08:00 会这道题不代表满足这个职位的其他要求啊。。。囧 | 
|      80echol      2016-12-21 13:14:27 +08:00 @Septembers 印象里有这个作用的 api 就是没想到,多谢 | 
|  |      82mqtt      2016-12-21 13:32:38 +08:00 还资深前端,我这 php 、前端都做的人都会写。 | 
|  |      83Rsl      2016-12-21 14:00:32 +08:00 这都不需要会吧, 会 google 就行... | 
|  |      84Tonni      2016-12-21 14:26:59 +08:00 不巧,前段时间去饿了么面试被问到了这个问题,不过上机时并没有让我做这道题,看到楼主把这件事发到 V2 上面了,吃过午饭花了四十分钟写完的: http://codepen.io/HouCoder/pen/MbxXVm 。 | 
|  |      85Lothar OP  1 @Tonni 给点小建议哈,题干里的 li 里面可能还有嵌套 ul ,#3 的文字内容不对,#4 同理, event.target 并不一定就是 li ,需要向上查找判断下。 | 
|  |      87a40049      2016-12-21 16:54:34 +08:00 这个很简单啊,饿了么的面试题如果这么简单的话感觉我也可以去啊。就算不用 DOM 库也只是简单的 classlist(如果不支持 classlist 就用 className & Regular Expression) parentNode removeChild  createElement addEventListener appendChild ,返回 index 稍微棘手点,需要配合 children 遍历。干脆你把全部的职位要求写出来,说不定我这次有机会呢。 | 
|  |      88davidzd      2016-12-21 17:17:49 +08:00 @Septembers 额删完了 第 500 个还是第 500 个么 | 
|  |      89spring5413      2016-12-21 17:31:22 +08:00 var oUl = document.getElementById("list"); oUl.className = oUl.className + " bar"; oUl.removeChild(oUl.getElementsByTagName("li")[3]); document.body.addEventListener("click", function(e) { if (e.target.tagName.toLowerCase() != "li") { return; } var self = e.target || e.srcElement, oParent = self.parentNode, children = oParent.children; for (var i = 0, l = children.length; i < l; i++) { if (children[i] == self) { alert(i); break; } } }, false); | 
|  |      90davidzd      2016-12-21 18:01:36 +08:00 其实 ES6 里面就这么简单啊 let listTest = document.getElementById('list'); listTest.className = 'bar'; listTest.children[9].remove(); listTest.children[498].innerHTML += 'v2ex'; listTest.addEventListener('click', event => { alert(Array.from(event.target.parentNode.children).indexOf(event.target)+1) }) 这题目唯一的陷阱就是不要让前面的操作打乱了序号啊。。 | 
|  |      91Septembers      2016-12-21 18:47:56 +08:00 @davidzd  注意措辞是 "删除第 10 个" "在第 500 个" 可以解释成 操作位于某个位置的元素 并 进行操作 并且没一段都没有上下文进行限定 如果带有这个限定的话 实现则可写成 const nth10 = document.querySelector('#list:nth-of-type(10n)') const nth500 = document.querySelector('#list:nth-of-type(500n)') 然后分别进行操作 | 
|  |      92bobsam      2016-12-21 22:34:58 +08:00 这道题。。。不是应届生标准题吗。。。做出来能有 P6 啦? | 
|  |      93halden      2016-12-22 00:53:34 +08:00 @Rice 我在美国面试了这么多,考察原生 js 的一般只涉及代码阅读,因为这里关于 js 的基础知识,要你写代码的至少给了 jQuery | 
|      95tidewind      2016-12-22 10:13:43 +08:00 用不用 jquery 也无所谓,原生 js 也可以搞啊,半小时,还可以 google ,我作为一个后端都可以去搞定。确定这是对标 P6 的面试题么... | 
|  |      96davidzd      2016-12-22 11:21:19 +08:00 @Septembers 对啊,肯定是跟着#No. 走的啊,不过这可能也不是考察重点哈哈哈 | 
|  |      97davidzd      2016-12-22 11:21:52 +08:00 这题用 jquery 干毛? | 
|  |      98501956430      2016-12-22 11:45:24 +08:00 via iPhone 用 jq 挺简单的 | 
|  |      99banxi1988      2016-12-22 11:53:58 +08:00  1 我也来参与一下: 解与说明: 1. 添加类. - 经典写法 `node.className = node.className + " bar"` - 新式写法 (IE10+) `node.classList.add("bar")` 拓展: `classList` 是一个只读属性,指向 `DOMTokenList` 还有如下方法: `add(String [,String])`, `remove(String[,String])`,`item(Number)`,`toggle(String[, force])` 2. 删除第 10 个 `<li>` - 经典写法: ```js var lilist = document.getElementsByTagName("li"); var li10 = lilist[9]; li10.parentNode.removeChild(li10); ``` - 新式写法: ```js var li10 = document.querySelector("li:nth-of-type(10)"); li10.parentNode.removeChild(li10); ``` 需要注意的是: nth 是以 1 based index. 而 数组是 0 based index. 3. 在第 500 个 <li> 后面增加一个 <li> , 其文字内容为 <v2ex.com /> ```js var v2exNode = document.createElement("li"); v2exNode.textContent = "<V2EX.com />"; var li501 = document.getElementsByTagName("li")[500]; li501.parentNode.insertBefore(v2exNode,li501); ``` 值得注意的是, DOM API 只有 insertBefore 没有 insertAfter 所以要先取到第 501 个. 4. 点击任意 <li> 弹窗显示其为当前列表中的第几项。 ```js var ul = document.getElementById("list"); ul.addEventListener("click",function(event){ var target = event.target; if(target.nodeName === "LI"){ var parentUl = target.parentNode; var children = parentUl.childNodes; var count = 0; for(var i = 0; i < children.length;i++){ var node = children[i]; if(node.nodeName === "LI"){ count += 1; if(node === target){ alert("是当前第"+(count)+"项"); break; } } } } }); ``` 我这里 给 `ul#list` 添加 click 方法然后判断 `target` 来实现的. 因为我不想添加太多的 eventListener. 值得注意的是: 需要通过 `childNodes` 来遍历. 因为 `li` 中还是可以再嵌套 `ul>li` 附: 生成测试 html 的脚本: ```py # -*- coding: utf-8 -*- import random __author__ = 'banxi' index = -1 def make_index(): global index index += 1 if random.uniform(1, 10) > 8: return '<span>#%d</span>' % index else: return "#%d" % index def make_ul(): html = '<ul>' for i in range(0, random.randint(1, 5)): html += make_li() html += '</ul>' return html def make_li(): if random.uniform(1, 10) < 1.5: inner_html = make_ul() else: inner_html = make_index() return "<li>%s</li>" % inner_html if __name__ == '__main__': import codecs with codecs.open('ele.html', 'w', encoding='utf-8') as f: html = '<ul id="list" class="foo">' while index < 100000: html += make_li() html += "</ul>" f.write(html) ``` | 
|  |      100galenyuan      2016-12-22 12:52:04 +08:00 http://jsbin.com/filecaw/edit?html,js,output 简单答一波=。= 第三题用到 insertAdjacentHTML 是现搜的,其他的基本都手答了 |