js节点的兼容性代码

1. 十二行基本代码

这十二行代码,都是获取节点或者元素的,但是后四组有兼容性的问题

	// 获取父节点
    console.log(ulObj.parentNode);
    // 获取父元素
    console.log(ulObj.parentElement);
    // 获取子节点
    console.log(ulObj.childNodes);
    // 获取子元素
    console.log(ulObj.children);

    console.log("----------------");
    // 获取第一个子节点
    console.log(ulObj.firstChild);   //IE8中是获取第一个子元素
    // 获取第一个子元素
    console.log(ulObj.firstElementChild);   //IE8中不支持,undefined
    // 获取最后一个子节点
    console.log(ulObj.lastChild);   //IE8中是获取最后一个子元素
    // 获取最后一个子元素
    console.log(ulObj.lastElementChild);   //IE8中不支持,undefined
    // 获取前一个兄弟节点
    console.log(liObj.previousSibling);   //IE8中是获取前一个兄弟元素
    // 获取前一个兄弟元素
    console.log(liObj.previousElementSibling);   //IE8中不支持,undefined
    // 获取后一个兄弟节点
    console.log(liObj.nextSibling);   //IE8中是获取后一个兄弟元素
    // 获取后一个兄弟元素
    console.log(liObj.nextElementSibling);   //IE8中不支持,undefined

2. 别人的思路

为解决获取元素的兼容性问题,需要编写兼容性代码
在网上有看到这样的代码
http://bbs.miaov.com/forum.php?mod=viewthread&tid=6789&highlight=


function first(obj){

        return obj.firstChild.nodeType == 1 ? obj.firstChild : next(obj.firstChild) 

}

function last(obj){

        return obj.lastChild.nodeType == 1 ? obj.lastChild : pre(obj.lastChild);

}


function next(obj){

        return obj.nextSibling.nodeType == 1 ? obj.nextSibling : next(obj.nextSibling);

}

function pre(obj){

        return obj.previousSibling.nodeType == 1 ? obj.previousSibling : pre(obj.previousSibling);

}

这个代码确实很简洁
思路也非常值得借鉴
不过在调试过程中,发现了一个BUG

先不说BUG,单讨论其中的思想

总的来讲,第一个孩子 最后一个孩子 前一个兄弟 后一个兄弟,会有兼容性问题
以获取第一个孩子元素为例
firstChild 得到的要么是第一个节点,要么是第一个标签
就这两种情况
如果获取到的是第一个标签
那么就返回
否则,就找第一个节点的下一个兄弟元素(就是第一个元素)

如此,便可以设计出以上的代码

3. 改进和优化

问题所在
在寻找兄弟元素的部分,使用了函数的嵌套调用

function next(obj){

        return obj.nextSibling.nodeType == 1 ? obj.nextSibling : next(obj.nextSibling);

}

function pre(obj){

        return obj.previousSibling.nodeType == 1 ? obj.previousSibling : pre(obj.previousSibling);

}

这里没有考虑函数嵌套调用的跳出条件
当obj.nextSibling.nodeType == 1始终不满足的时候
或者说没有下一个兄弟节点的时候
最终就会是(null).nodeType==1?
对null使用.nodeType,这样浏览器会报错
以下是个人改进版的代码:

/**
 * 获取第一个孩子元素
 * @param element 父节点,DOM对象
 * @returns {ChildNode | (() => (Node | null)) | ActiveX.IXMLDOMNode} 返回的是获取到的孩子元素,没有找到,返回null
 */
function getFirstElementChild(element) {
    return element.firstChild.nodeType == 1 ? element.firstChild : getNextElementSibling(element.firstChild);
}

/**
 * 获取最后一个孩子元素
 * @param element 父节点,DOM对象
 * @returns {ActiveX.IXMLDOMNode | ChildNode | (() => (Node | null))} 返回的是获取到的孩子元素,没有找到,返回null
 */
function getLastElementChild(element) {
    return element.lastChild.nodeType == 1 ? element.lastChild : getPreviousElementSibling(element.lastChild);
}

/**
 * 获取后一个兄弟元素
 * @param element element 当前节点,DOM对象
 * @returns {*} 返回的是获取到的后一个兄弟元素,没有后一个兄弟节点,返回null
 */
function getNextElementSibling(element) {
    // 如果没有下一个兄弟节点,那么直接返回null,否则对null进行nodeType,会报错
    if (element.nextSibling == null)
        return null;
    return element.nextSibling.nodeType == 1 ? element.nextSibling : getNextElementSibling(element.nextSibling)
}

/**
 * 获取前一个兄弟元素
 * @param element element 当前节点,DOM对象
 * @returns {*} 返回的是获取到的前一个兄弟元素,没有前一个兄弟节点,返回null
 */
function getPreviousElementSibling(element) {
    // 如果没有前一个兄弟节点,那么直接返回null,否则对null进行nodeType,会报错
    if (element.previousSibling == null)
        return null;
    return element.previousSibling.nodeType == 1 ? element.previousSibling : getPreviousElementSibling(element.previousSibling)
}

/**
 * 获取元素所有的兄弟元素(包括它本身)
 * @param element 待使用元素
 * @returns {Array} 返回一个数组,包含所有兄弟元素
 */
function getAllElementsSibling(element) {
    var parNode = element.parentNode;
    // 用最后一个孩子,然后向上遍历也可以
    var currentNode = getFirstElementChild(parNode);
    var allSiblings = [];
    for (var i = 0; currentNode; i++) {
        allSiblings[i] = currentNode;
        currentNode = getNextElementSibling(currentNode);
    }
    return allSiblings;
}

4. 小结

碰到函数的嵌套调用,要考虑函数的跳出条件,否则很危险

扫描二维码关注公众号,回复: 4332020 查看本文章

猜你喜欢

转载自blog.csdn.net/ckxkobe/article/details/84707453