zhangxinxu / quiz

小测答题收集区
535 stars 43 forks source link

DOM基础测试29 #15

Open zhangxinxu opened 5 years ago

zhangxinxu commented 5 years ago

本期DOM小测题目如下:

大家提交回答的时候,注意缩进距离,起始位置从左边缘开始;另外,github自带代码高亮,所以请使用下面示意的格式。

```js
// 你的JS代码写在这里
 ```
les-lee commented 5 years ago
  1. 
    if (!Element.prototype.matches) {
    Element.prototype.matches = Element.prototype.msMatchesSelector || 
                              Element.prototype.webkitMatchesSelector;
    }

if (!Element.prototype.closest) { Element.prototype.closest = function(s) { var el = this;

do {
  if (el.matches(s)) return el;
  el = el.parentElement || el.parentNode;
} while (el !== null && el.nodeType === 1);
return null;

}; }


没听过这个API,搜了一下MDN 没想到就有答案了... 

2. 
```js
if (!Element.prototype.matches) {
  Element.prototype.matches = Element.prototype.msMatchesSelector || 
                              Element.prototype.webkitMatchesSelector;
}

  Element.prototype.closestAll = function(s) {
    var el = this;
    var closests = [];
    do {
      if (el.matches(s)) {
           closests.push(el)
       } 
      el = el.parentElement || el.parentNode;
    } while (el !== null && el.nodeType === 1);
    return closests.length > 0 ? closests : null;
  };
liyongleihf2006 commented 5 years ago
//matches的polyfill
if (!Element.prototype.matches) {
        Element.prototype.matches = 
        Element.prototype.matchesSelector || 
        Element.prototype.mozMatchesSelector ||
        Element.prototype.msMatchesSelector || 
        Element.prototype.oMatchesSelector || 
        Element.prototype.webkitMatchesSelector ||
        function(s) {
            var matches = (this.document || this.ownerDocument).querySelectorAll(s),
                i = matches.length;
            while (--i >= 0 && matches.item(i) !== this) {}
            return i > -1;            
        };
}
//第一题
if (!Element.prototype.closest){
    Element.prototype.closest = function(s) {
        var el = this;
        if (!((el.document || el.ownerDocument.documentElement)).contains(el)){
            return null;
        } 
        do {
            if (el.matches(s)) return el;
            el = el.parentElement;
        } while (el !== null);
        return null;
    };
}
//第二题
if (!Element.prototype.closestAll){
    Element.prototype.closestAll = function(s) {
        var el = this,
            closests = [];
        if (!((el.document || el.ownerDocument.documentElement)).contains(el)){
            return closests;
        } 
        do {
            if (el.matches(s)){
                closests.push(el);
            };
            el = el.parentElement;
        } while (el !== null);
        return closests;
    };
}
XboxYan commented 5 years ago
//Element.matches
if (!Element.prototype.matches) {
    Element.prototype.matches =
        Element.prototype.matchesSelector ||
        Element.prototype.mozMatchesSelector ||
        Element.prototype.msMatchesSelector ||
        Element.prototype.oMatchesSelector ||
        Element.prototype.webkitMatchesSelector
}
//Element.closest
if (!Element.prototype.closest) {
    Element.prototype.closest = function(selector) {
        var element = this;
        while (element && element.nodeType === 1) {
            if (element.matches(selector)) {
                return element;
            }
            element = element.parentNode;
        }
        return null;
    };
}
//Element.closestAll
Element.prototype.closestAll = function (selector) {
    var element = this;
    var nodeList = [];
    while (element && element.nodeType === 1) {
        if (element.matches(selector)) {
            nodeList.push(element);
        }
        element = element.parentNode;
    }
    return nodeList;
};
smileyby commented 5 years ago

要不是这个测试,都不知道还有这方法

Element.prototype.myClosest = function myClosest(value){
  if (value === '') {
    return null;
  }

  if (this.closest) {
    return this.closest(value);
  } 

  let nodeAry = document.querySelectorAll(value),
      nodeLength = nodeAry.length,
      lastCloseNode = null;
  if (nodeLength <= 0) {
    return null;
  }

  if (nodeLength === 1 && nodeAry[0] === this) {
    return this;
  }

  for (let i = 0; i < nodeLength; i += 1) {
    if (nodeAry[i].contains(this)) {
      lastCloseNode = nodeAry[i];
    }
  }
  return lastCloseNode;
};
Element.prototype.closestAll = function closestAll(value){
  if (value === '') {
    return [];
  }

  let nodeAry = document.querySelectorAll(value),
      nodeLength = nodeAry.length,
      allCloseNode = [];
  if (nodeLength <= 0) { 
    return [];
  }

  for (let i = 0; i < nodeLength; i += 1) {
    if (nodeAry[i].contains(this) || nodeAry[i] === this) { 
      allCloseNode.push(nodeAry[i]);
    }
  }
  return allCloseNode;
};
Fatty-Shu commented 5 years ago
// 题1
if (!Element.prototype.closest) {
    if(!Element.prototype.matches){ 
        Element.prototype.matches = Element.prototype.msMatchesSelector
    }
    Element.prototype.closest = function (selector) {
        let el = this
        do {
            if (el.matches(selector)) return el;
            el = el.parentElement
        } while (el && el.nodeType === Node.ELEMENT_NODE)
        return null
    }
}
// 题二
// zhangxinxu: 有bug,本题无分
Element.prototype.closestAll = function (selector) {
    let NodeLists = []
    let el = this
    let selected = null
    do {
        selected = Element.prototype.closest.call(el, selector)
        selected && NodeLists.push(selected) && (el = el.parentElement)
    } while (selected && el && el.nodeType === Node.ELEMENT_NODE)
    return NodeLists
}
zhuiyue132 commented 5 years ago
   ;(function() {
        if (!Element.prototype.matches) {
          Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector
        }

        if (!Element.prototype.closest) {
          Element.prototype.closest = function(selector) {
            const el = this
            let current = this
            if (!selector || el.nodeType !== 1) return null
            if (current.matches(selector)) return current
            while (current.parentElement || current.parentNode) {
              current = current.parentElement || current.parentNode
              if (current.matches(selector)) return current
            }
            return null
          }
        }
        // zhangxinxu: 没对current做合法性判断,导致无匹配选择器时候出错,本题无分
        Element.prototype.closestAll = function(selector) {
          const el = this
          let current = this
          let closestList = []
          if (!selector || el.nodeType !== 1) return closestList
          if (current.matches(selector)) closestList.push(current)
          while (current.parentElement || current.parentNode) {
            current = current.parentElement || current.parentNode
            if (current.matches(selector)) closestList.push(current)
          }
          return closestList
        }
    })()
magicds commented 5 years ago

matches 部分 两个都需要

// Polyfill for Element.prototype.matches
if (!Element.prototype.matches) {
    var ep = Element.prototype;
    ep.matches = ep.msMatchesSelector || ep.webkitMatchesSelector || ep.mozMatchesSelector || ep.oMatchesSelector;
    if (!ep.matches) {
        ep.matches = function () {
            throw new Error('浏览器实在太老,该换代了!!!');
        };
    }
    ep = null;
}

1. Polyfill for Element.prototype.closest

if (!window.Element.prototype.closest) {
    window.Element.prototype.closest = function (selector) {
        if (selector === void(0)) {
            throw new TypeError("Failed to execute 'closest' on 'Element': 1 argument required, but only 0 present.");
        }
        if (selector + '' !== selector) {
            throw new TypeError("Failed to execute 'closest' on 'Element': the argument must be string.");
        }
        var el = this;
        do {
            if (el.matches(selector)) {
                return el;
            }
            el = el.parentElement || el.parentNode;
        } while (el !== null && el.nodeType === Node.ELEMENT_NODE)
        return null;
    }
}

2. Polyfill for Element.prototype.closestAll

if (!window.Element.prototype.closestAll) {
    window.Element.prototype.closestAll = function (selector) {
        if (selector === void(0)) {
            throw new TypeError("Failed to execute 'closest' on 'Element': 1 argument required, but only 0 present.");
        }
        if (selector + '' !== selector) {
            throw new TypeError("Failed to execute 'closest' on 'Element': the argument must be string");
        }
        var el = this;
        var arr = [];
        do {
            if (el.matches(selector)) {
                arr.push(el);
            }
            el = el.parentElement || el.parentNode;
        } while (el !== null && el.nodeType === Node.ELEMENT_NODE)
        return arr;
    }
}
guqianfeng commented 5 years ago
    (function(){
        //Element.matches
        if(!Element.prototype.matches){
            Element.prototype.matches =
                Element.prototype.matchesSelector ||
                Element.prototype.msMatchesSelector ||
                Element.prototype.webkitMatchesSelector ||
                Element.prototype.oMatchesSelector ||
                Element.prototype.mozMatchesSelector;

        }

        //Element.closest
        if(!Element.prototype.closest){
            Element.prototype.closest = function(selector){
                var el = this;
                if(!document.documentElement.contains(el)){
                    return null;
                }
                do{
                    if(el.matches(selector)) return el;
                    el = el.parentElement;
                }while(el != null)
                return null;
            }
        }

        //Element.closestAll
        Element.prototype.closestAll = function(selector){
            var el = this;
            var closestArr = [];
            do{
                if(el.matches(selector)){
                    closestArr.push(el)
                }
                el = el.parentElement;
            }while(el != null);
            return closestArr;
        }
    })()
lineforone commented 5 years ago
 // 1
  if(!Element.prototype.closest){
    Element.prototype.closest = function (selector) {
      if (!arguments.length) {
        throw new Error("Failed to execute 'closest' on 'Element': 1 argument required, but only 0 present.")
        return
      }
      var el = this

      while (el && el.ndoeType === 1) {
        el = el.parentNode || el.parentElement
        if (el.matches(selector)) {
          return el
        }
      }
      return null
    }
  }

  // 2
 // zhangxinxu: 有非常严重的bug,会死循环,本题无分
  Element.prototype.closestAll = function (selector) {
    var el = this,
      closestEls = [];
    while (el) {
      el = el.closest(selector)
      if (el) 
        closestEls.push(el)
    }
    return closestEls
  }
y389278443z commented 5 years ago

ie9都支持 matches了, 不知道为啥上面很多还要写一个matches。

if (!Element.prototype.closest) {
        Element.prototype.closest = function (s) {
            var el = this;
            if (!document.documentElement.contains(el)) return null;
            do {
                if (el.matches(s)) return el;
                el = el.parentNode;
            } while (el !== null);
            return null;
        }
}

    Element.prototype.closestAll = function (s) {
        var el = this,
            resultArr = [];
        if (!document.documentElement.contains(el)) return null;
        while(el.nodeType === 1) {
            if (el.matches(s)) resultArr.push(el);
            el = el.parentNode;
        }
        return resultArr;
    };
ZWkang commented 5 years ago
if (!Element.prototype.closest){
    Element.prototype.closest = function (selector) {
        if (!(this instanceof HTMLElement)) return null
        var now = this.parentElement
        while(now !== null ){
            if(now.matches(selector) && now.nodeType === Node.ELEMENT_NODE){
                return now
            }
            now = now.parentElement
        }
        return null
    }
}
Element.prototype.closestAll = function (selector) {
    if (!(this instanceof HTMLElement)) return null
    var now = this.parentElement, result = []
    while(now !== null ){
        if(now.matches(selector) && now.nodeType === Node.ELEMENT_NODE) {
            result.push(now)
        }
        now = now.parentElement
    }
    return result
}
hutuchong9527 commented 5 years ago
if (!Element.prototype.matches) {
    Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
}
/*第一题*/
if (!Element.prototype.closest) {
    Element.prototype.closest = function (s) {
        var el = this;
        do {
            if (el.matches(s)) return el;
            el = el.parentElement;
        } while (el !== null);
        return null;
    }
}
/*第二题*/
Element.prototype.closestAll = function (s) {
    var el = this;
    var output = [];
    do {
        if (el.matches(s)) {
            output.push(el);
        };
        el = el.parentElement;
    } while (el !== null);
    return output.length === 0 ? null : output;
}
xunzhaoshitouyu commented 5 years ago
(function() {
    // closest
    if (!Element.prototype.closest) {
        Element.prototype.closest = function(selector) {
            var nodeList = document.querySelectorAll(selector);
            var targetElement;
            var flag = false;
            var findTarget = function(ele) {
                if (nodeList.length > 0) {
                    for (var i = 0; i < nodeList.length; i++) {
                        if (ele == nodeList[i]) {
                            targetElement = ele;
                            flag = true;
                        }
                    }
                    if (!flag){
                        findTarget(ele.parentElement);
                    }
                } else { // 没有匹配的元素
                    return null;
                }
            }
            findTarget(this);
            return targetElement;
        }
    }

    // closestAll 第二题我理解的是获取第一题中匹配元素的所有祖先元素
    Element.prototype.closestAll = function(selector) {
        var _this = this.closest(selector); // 获取到当前匹配元素
        // 查找匹配元素的所有祖先元素
        var parentElements = [];
        var findParent = function(ele) {
            if (ele.parentElement) {
                parentElements.push(ele.parentElement);
                findParent(ele.parentElement);
            }
        }
        findParent(_this);
        return parentElements;
    }

    // closestAll 如果第二题是返回所有匹配元素的话
    Element.prototype.closestAll = function(selector) {
        return document.querySelectorAll(selector);
    }
})();
RichardDFang commented 5 years ago
        if (!Element.prototype.matches) {
            Element.prototype.matches = Element.prototype.msMatchesSelector ||
                Element.prototype.webkitMatchesSelector;
        };

        if (!Element.prototype.closest) {
            Element.prototype.closest = function(selector) {
                let element = this;
                if (typeof selector !== 'string') {
                    return null;
                };

                if (!document.querySelector(selector)) {
                    return null;
                };

                while (element) {
                    if (element.matches(selector)) {
                        return element;
                    }

                    element = element.parentElement;
                };

                return null;
            };
        }

        Element.prototype.closestAll = function(selector) {
            let element = this;
            if (typeof selector !== 'string') {
                return [];
            }

            if (!document.querySelectorAll(selector).length) {
                return [];
            }

            let closestParents = [];
            while (element) {
                if (element.matches(selector)) {
                    closestParents.push(element);
                }

                element = element.parentElement;
            }

            return closestParents;
        }
Seasonley commented 5 years ago
if (!Element.prototype.matches)
    Element.prototype.matches = Element.prototype.msMatchesSelector ||
                                Element.prototype.webkitMatchesSelector

if (!Element.prototype.closest)
    Element.prototype.closest = function (s) {
        var el = document.documentElement.contains(this)?this:null
        while (el && el.nodeType===1 && !el.matches(s))
            el = el.parentElement || el.parentNode
        return el
    }

Element.prototype.closestAll = function (s) {
    var el = document.documentElement.contains(this)?this:null
    for(var closests = []; el&&el.nodeType===1; el=el.parentElement||el.parentNode)
        el.matches(s) && closests.push(el)
    return closests.length>0 ? closests : null
}

特地看了看 Difference between DOM parentNode and parentElement - stackoverflow In Internet Explorer, parentElement is undefined for SVG elements, whereas parentNode is defined. 然后我手动测了下继承关系:

NeilChen4698 commented 5 years ago
Element.prototype.closest = function(selector) {
    let nodeList = [].slice.call(this.ownerDocument.querySelectorAll(selector));
    let firstChildren = [].slice.call(this.ownerDocument.childNodes);
    if (nodeList.length === 0) {
        return null;
    } else {
        let ele = this;
        while (nodeList.indexOf(ele) < 0 && firstChildren.indexOf(ele) < 0) {
            ele = ele.parentElement;
        }
        return nodeList.indexOf(ele) < 0 ? null : ele;
    }
}

Element.prototype.closestAll = function(selector) {
    let nodeList = [].slice.call(this.ownerDocument.querySelectorAll(selector));
    let firstChildren = [].slice.call(this.ownerDocument.childNodes);
    if (nodeList.length === 0) {
        return [];
    } else {
        let ele = this, result = [];
        while (firstChildren.indexOf(ele) < 0) {
            if (nodeList.indexOf(ele) >= 0) {
                result.push(ele)
            }
            ele = ele.parentElement;
        }
        return result;
    }
}
frankyeyq commented 5 years ago

表示这个方法没见过,查了mdn文档之后发现matchs也没见过,然后弄懂了。 matchs的作用是判断调用他的dom是否能被这个表达式找到,closest返回第一个满足matchs为true的自身或者自身的祖先元素。既然mdn都有答案了,那就学习一下对于closest和matchs的polyfill的正确答案吧。

1. matchs polyfill

if (!Element.prototype.matches) {
  Element.prototype.matches = 
      Element.prototype.matchesSelector || 
      Element.prototype.mozMatchesSelector ||
      Element.prototype.msMatchesSelector || 
      Element.prototype.oMatchesSelector || 
      Element.prototype.webkitMatchesSelector ||
      function(s) {
        var matches = (this.document || this.ownerDocument).querySelectorAll(s),
            i = matches.length;
        while (--i >= 0 && matches.item(i) !== this) {}
        return i > -1;            
      };
}

2. closest polyfill

if (!Element.prototype.closest) {
  Element.prototype.closest = function(s) {
    var el = this;
    do {
      if (el.matches(s)) return el;
      el = el.parentElement || el.parentNode;
    } while (el !== null && el.nodeType === 1); // 这里是为了向上遍历的时候排除document节点
    return null;
  };
}

3. closestAll

Element.prototype.closestAll = function(domString) {
    var el = this;
    var NodeList = [];
    while(el && el.nodeType === 1) {
        if (el.matches(domString)) NodeList.push(el);
        el = el.parentElement || el.parentNode;
    }
    return NodeList;
}
thewindsword commented 5 years ago
(function() {
  // matches 的 polyfill
  if (!Element.prototype.matches) {
    Element.prototype.matches = 
        Element.prototype.matchesSelector || 
        Element.prototype.mozMatchesSelector ||
        Element.prototype.msMatchesSelector || 
        Element.prototype.oMatchesSelector || 
        Element.prototype.webkitMatchesSelector ||
        function(s) {
            var matches = (this.document || this.ownerDocument).querySelectorAll(s),
                i = matches.length;
            while (--i >= 0 && matches.item(i) !== this) {}
            return i > -1;            
        };
  }
  if (!Element.prototype.closest) {
    // 第一题
    Element.prototype.closest = function (selector) {
      let el = this;
      while (el && el.nodeType === 1) {
        if (el.matches(selector)) {
          return el;
        }
        el = el.parentNode;
      }
      return null;
    }
    // 第一题 END
  }
  if (!Element.prototype.closestAll) {
    // 第二题
    Element.prototype.closestAll = function (selector) {
      let el = this.parentNode;
      let nodeList = null;
      while (el && el.nodeType === 1) {
        if (el.matches(selector)) {
          if (!nodeList) {
            nodeList = [];
          }
          nodeList.push(el);
        }
        el = el.parentNode;
      }
      return nodeList;
    }
    // 第二题 END
  }
})()
wingmeng commented 5 years ago

我的回答:用 jQuery 的 closest() 方法。开个玩笑 :laughing:

先说下思路:根据传入的参数,使用 querySelectorAll() 方法找出所有匹配的选择器,再匹配出包含当前元素的 nodeList 列表。

第二题

第一题依赖这个方法,所以它先上


Element.prototype.closestAll = function(targetEl) {
var el = this;
var nodelist;

// 参数校验 if (typeof targetEl !== 'string' || !targetEl.trim()) { throw Error('\' + targetEl + \' is not a valid selector'); }

nodelist = document.querySelectorAll(targetEl);

// 使用 ES5 的 filter 过滤出包含 el 的元素 return Array.prototype.slice.call(nodelist) .filter(function(node) { return node.contains(el) }) .reverse(); // 反转数组,最近的排前面,依次从近到远 }


#### 第一题
> 获取 `closestAll()` 方法返回的第一项即可
``` js
// closest polyfill
window.Element && 'closest' in Element.prototype || +function() {
  Element.prototype.closest = function(targetEl) {
    var result = this.closestAll(targetEl);
    return result.length === 0 ? null : result[0];
  }
}();
silverWolf818 commented 5 years ago

开始工作使用的是jquery的closest方法,通过查询mdn文档得知Element对象上也存在一个closest方法,同时官方提供了一个很好的polyfill方法,也算是学习到了。closestAll也是稍微改造一下也可以实现。

if (!Element.prototype.matches)
    Element.prototype.matches = Element.prototype.msMatchesSelector ||
                                Element.prototype.webkitMatchesSelector;

if (!Element.prototype.closest)
    Element.prototype.closest = function(s) {
        var el = this;
        if (!document.documentElement.contains(el)) return null;
        do {
            if (el.matches(s)) return el;
            el = el.parentElement;
        } while (el !== null);
        return null;
    };
  Element.prototype.closestAll = function(s) {
        var el = this;
        var arr = [];
        if (!document.documentElement.contains(el)) return null;
        do {
            if (el.matches(s)){
             arr.push(el); 
            };
            el = el.parentElement;
        } while (el !== null);
        return (arr.length > 0 ? arr : null);
    };
ylfeng250 commented 5 years ago

这次混一个参与分就好了,大家写的都大同小异,感觉再写一样的就没啥意思了。我有一种思路,主要是要 做事件的兼容性处理(偷懒没有做),其他的应该没啥问题,仅供参考交流。

if (!Element.prototype.closestAll) {
  Element.prototype.closestAll = function (selector) {
    const self = this
    var path = []
    function getPath(e) {
      // 通过事件冒泡传播的路径来获取祖先元素
      for (let i = 0, length = e.path.length - 2; i < length; i++) {
        if(e.path[i].matches(selector)) {
          path.push(e.path[i])
        }
      }
      // 移除事件监听
      self.removeEventListener('my-closest-all', getPath)
    }
    // 创建一个 冒泡但是不能取消的事件
    var ev = new Event('my-closest-all', { 'bubbles': true, 'cancelable': false })
    // 监听这个事件
    self.addEventListener('my-closest-all', getPath)
    // 派发这个事件
    self.dispatchEvent(ev)
    return path
  }
}
if (!Element.prototype.closest) {
  Element.prototype.closest = function (selector) {
    const self = this
    var path = null
    function getPath(e) {
      // 通过事件冒泡传播的路径来获取祖先元素
      for (let i = 0, length = e.path.length - 2; i < length; i++) {
        if(e.path[i].matches(selector)) {
          path = e.path[i]
          break
        }
      }
      // 移除事件监听
      self.removeEventListener('my-closest', getPath)
    }
    // 创建一个 冒泡但是不能取消的事件
    var ev = new Event('my-closest', { 'bubbles': true, 'cancelable': false })
    // 监听这个事件
    self.addEventListener('my-closest', getPath)
    // 派发这个事件
    self.dispatchEvent(ev)
    return path
  }
}
littleKnave commented 5 years ago

先上mdn答案

// MDN提供polyfill
if (!Element.prototype.matches) {
  Element.prototype.matches = Element.prototype.msMatchesSelector ||
    Element.prototype.webkitMatchesSelector;
}

if (!Element.prototype.closest) {
  Element.prototype.closest = function(s) {
    var el = this;
    if (!document.documentElement.contains(el)) {
      return null;
    } 
    do {
        if (el.matches(s)) {
          return el;
        } 
        el = el.parentElement;
    } while (el !== null);
    return null;
  };
}

接下来自己写的

/**
 * 1.closest匹配特定选择器且离当前元素最近的祖先元素
 * 2.选择器错误给错误提示
 * 3.查询不到返回null
 *  */ 
if (!Element.prototype.closest) {
  Element.prototype.myClosest = function(select) {
    try {
      // 全部的选择器元素;
      const selectDom = document.querySelectorAll(select);
      const selectDomLength = selectDom.length;
      // 没有特定选择器元素,直接返回null
      if (selectDomLength === 0) {
        return null
      } 
      // 当前元素第一个父元素
      let el = this.parentElement;
      if (el === null) {
        return null;
      }

      do {
        for (let i = 0; i < selectDomLength; i++) {
          if (el === selectDom[i]) {
            return el;
          }
        }
        // 不用parentNode,因为到最外层parentNode会返回#document,parentElement返回null
        el = el.parentElement;
      } while (el !== null)
    } catch(error) {
      // 选择器异常,抛出错误
      throw new SyntaxError(`Failed to execute 'myClosest' on 'Element': '${select}' is not a valid selector.`);
    }
  }
}

closestAll的思路也差不多,只是返回值变成了数组

Element.prototype.closestAll = function(select) {
  try {
    // 全部的选择器元素;
    const selectDom = document.querySelectorAll(select);
    const selectDomLength = selectDom.length;
    const nodeList = [];

    // 没有特定选择器元素,直接返回null
    if (selectDomLength === 0) {
      return nodeList
    } 
    // 当前元素第一个父元素
    let el = this.parentElement;
    if (el === null) {
      return nodeList;
    }

    do {
      for (let i = 0; i < selectDomLength; i++) {
        if (el === selectDom[i]) {
          nodeList.push(el);
        }
      }
      // 不用parentNode,因为到最外层parentNode会返回#document,parentElement返回null
      el = el.parentElement;
    } while (el !== null)
    return nodeList;
  } catch(error) {
    // 选择器异常,抛出错误
    throw new SyntaxError(`Failed to execute 'myClosest' on 'Element': '${select}' is not a valid selector.`);
  }
}
uaison commented 5 years ago
// zhangxinxu: 有bug
Element.prototype.closest = Element.prototype.closest || function(selector) {
  /* 修改开始: 若没传参,返回最近父级元素 */
  if(!selector) return this.parentNode;
  // if(!selector) return this;
  /* 修改结束 */

  var el = document.querySelectorAll(selector);

  if(el.length === 0) return null;

  function getParentNode(node) {
    var parent = node.parentNode;
    if(parent.nodeType === 9) return null;
    for(var i=0;i<el.length;i++){
      if(parent === el[i]) return parent;
    }
    /* 修改开始,原来少了return */
    return getParentNode(parent);
    // getParentNode(parent);
    /* 修改结束 */
  }
  return getParentNode(this);
}

// zhangxinxu: 我测下来结果相差甚远,本题无分
Element.prototype.closestAll = Element.prototype.closestAll || function(selector) {
  var result = [];
   /* 修改开始: 若没传参,返回最近父级元素 */
  if(!selector) return this.parentNode? [this.parentNode] : [];
  // if(!selector) return [this];
  /* 修改结束 */

  var el = document.querySelectorAll(selector);

  if(el.length === 0) return [];

  function getParentNode(node) {
    var parent = node.parentNode;
    if(parent.nodeType === 9) return result;
    for(var i=0;i<el.length;i++){
      if(parent === el[i]) {
        result.push(parent);
        break;
      }
    }
    /* 修改开始,原来少了return */
    return getParentNode(parent);
    // getParentNode(parent);
    /* 修改结束 */
  }
  return getParentNode(this);
}
wind1996 commented 5 years ago
if (!Element.prototype.matches) {
    var eleProtoType = Element.prototype
    eleProtoType.matches =
        eleProtoType.webkitMatchesSelector ||
        eleProtoType.msMatchesSelector ||
        eleProtoType.mozMatchesSelector ||
        eleProtoType.oMatchesSelector||
        function(s) {
            var matches = (this.document || this.ownerDocument).querySelectorAll(s),
                i = matches.length;
            while (--i >= 0 && matches.item(i) !== this) {}
            return i > -1;            
        };
}

if (!Element.prototype.closest || true) {
    Element.prototype.closest = function (s) {
        if (!s) {
            throw Error('errMsg')
        }
        var el = this;
        if (!document.documentElement.contains(el)) {
            return null;
        }
        for (; el !== null;) {
            if (el.matches(s)) {
                return el;
            }
            el = el.parentElement
        }
        return null
    }
}

Element.prototype.closestAll = function (s) {
    if (!s) {
        throw Error('errMsg')
    }
    var el = this;
    if (!document.documentElement.contains(el)) {
        return null;
    }
    var result = [];
    for (; el !== null;) {
        if (el.matches(s)) {
            result.push(el);
        }
        el = el.parentElement
    }
    return result
};
zhangxinxu commented 5 years ago

本期小测学习目的:

  1. 了解Element.closest()原生API方法;
  2. 了解Element,matches()元素API方法,非常有用的API;
  3. 学会自定义扩展需要的Element API。
jsweber commented 5 years ago
    if (!Element.prototype.matches) Element.prototype.matches = webkitMatchesSelector || msMatchesSelector || mozMatchesSelector

    if (!Element.prototype.closest){
        Element.prototype.closest = function(s){
            if (!document.documentElement.contains(this)) return null
            let cur = this
            do{
                if (cur.matches(s)) return cur
                cur = cur.parentElement
            }while(cur)
            return null
        }
    }

    if (!Element.prototype.closestAll){
        Element.prototype.closestAll = function(s){
            if (!document.documentElement.contains(this)) return null
            let cur = this, ret = []

            do{
                if (cur.matches(s)) ret.push(cur)
                cur = cur.parentElement
            }while(cur)

            return ret
        }
    }