quiz icon indicating copy to clipboard operation
quiz copied to clipboard

DOM基础测试29

Open zhangxinxu opened this issue 5 years ago • 26 comments

本期DOM小测题目如下:

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

```js
// 你的JS代码写在这里
 ```

zhangxinxu avatar Mar 20 '19 10:03 zhangxinxu

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 没想到就有答案了...

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;
  };

les-lee avatar Mar 20 '19 10:03 les-lee

//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;
    };
}

liyongleihf2006 avatar Mar 20 '19 13:03 liyongleihf2006

//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;
};

XboxYan avatar Mar 20 '19 13:03 XboxYan

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

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;
};

smileyby avatar Mar 20 '19 15:03 smileyby

// 题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
}

Fatty-Shu avatar Mar 21 '19 02:03 Fatty-Shu

   ;(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
        }
    })()

zhuiyue132 avatar Mar 21 '19 02:03 zhuiyue132

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;
    }
}

magicds avatar Mar 21 '19 03:03 magicds

    (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;
        }
    })()

guqianfeng avatar Mar 21 '19 03:03 guqianfeng

 // 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
  }

lineforone avatar Mar 21 '19 03:03 lineforone

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;
    };

y389278443z avatar Mar 21 '19 03:03 y389278443z

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
}

ZWkang avatar Mar 21 '19 08:03 ZWkang

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;
}

hutuchong9527 avatar Mar 21 '19 08:03 hutuchong9527

(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);
	}
})();

xunzhaoshitouyu avatar Mar 21 '19 08:03 xunzhaoshitouyu

        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;
        }

RichardDFang avatar Mar 21 '19 11:03 RichardDFang

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. 然后我手动测了下继承关系:

  • EventTarget
    • Node(chrome 把 parentElement 放这)
      • Element
        • SVGElement
        • HTMLElement(IE 把 parentElement 放这)

Seasonley avatar Mar 22 '19 01:03 Seasonley

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;
	}
}

NeilChen4698 avatar Mar 22 '19 02:03 NeilChen4698

表示这个方法没见过,查了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;
}

frankyeyq avatar Mar 22 '19 03:03 frankyeyq

(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
  }
})()

thewindsword avatar Mar 22 '19 06:03 thewindsword

我的回答:用 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() 方法返回的第一项即可

// 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];
  }
}();

wingmeng avatar Mar 22 '19 06:03 wingmeng

开始工作使用的是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);
    };

silverWolf818 avatar Mar 22 '19 09:03 silverWolf818

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

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
  }
}

ylfeng250 avatar Mar 22 '19 10:03 ylfeng250

先上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.`);
  }
}

littleKnave avatar Mar 22 '19 13:03 littleKnave

// 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);
}

uaison avatar Mar 22 '19 14:03 uaison

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
};

wind1996 avatar Mar 22 '19 16:03 wind1996

本期小测学习目的:

  1. 了解Element.closest()原生API方法;
  2. 了解Element,matches()元素API方法,非常有用的API;
  3. 学会自定义扩展需要的Element API。

zhangxinxu avatar Mar 23 '19 02:03 zhangxinxu

    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
        }
    }

jsweber avatar Mar 29 '19 03:03 jsweber