空文本节点和内联块布局

Empty Text Node & Inline-Block Layout

本文关键字:布局 文本 节点      更新时间:2023-09-26

使用以下标记

<ul>
    <li>
        <label class="field">
            Some Stuff 1
        </label>
        <input class="value" type="text" />
    </li>
    <li>
        <label class="field">Some Stuff 2</label>
        <input class="value" type="text" />
    </li>
    <li>
        <label class="field">Some Stuff 3</label>
        <input class="value" type="text" />
    </li>
    <li>
        <label class="field">Some Stuff 4</label>
        <input class="value" type="text" />
    </li>
    <li>
        <label class="field">Some Stuff 5</label>
        <input class="value" type="text" />
    </li>
    <li>
        <label class="field">Some Stuff 6</label>
        <input class="value" type="text" />
    </li>
    <li>
        <label class="field">Some Stuff 7</label>
        <input class="value" type="text" />
    </li>
    <li>
        <label class="field">Some Stuff 8</label>
        <input class="value" type="text" />
    </li>
</ul>

使用以下 CSS

html, body {
    width: 100%;
}
* {
    margin: 0;
    padding: 0;
    border: 0;
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
}
input {
    border: 1px solid;
}
ul {
    list-style: none;
    width: 100%;
}
ul > li {
    display: inline-block;
    width: 25%;
    padding: 3px 8px;
}
.field,
.value {
    display: block;
    width: 100%;
}

请不要介意这是多么可怕,只是为了证明问题。

您会期望连续有 4 个li,因为没有边距填充或边框来占用它们可用的任何空间,并且每个空间的大小都为父ul的 25%。

但是因为我们都喜欢让我们的标记可读,而不仅仅是把它全部放在一行中,所以我们添加了额外的分隔符和制表符/空格来缩进 ect...但这最终在 DOM 中作为空文本节点,这些当然是完全自然的并且应该存在,但由于它们占用了 4px 的空间,确切地说,它们破坏了利用内联块的良好流畅布局。

我不喜欢在进行流畅布局时浮动元素,我喜欢使用display: inline-block而是空文本节点存在这个烦人的问题。

现在我对我使用女巫的解决方案有一个摆弄,那就是使用 javascript 删除它们,您可以在此处或 http://jsfiddle.net/Tt9zn/5/

查看
$(function() {
    function tirmSpecial(str) {
        // Trim 'r Carriage Return, 'n Linefeed, 't Tab, 'v Vertical Tab, 'f Form-Feed, 'u0020 Space
        // Do Not Trim (u00A0 = &nbsp)
        return str.replace(/^['r'n't'v'f'u0020]+|['r'n't'v'f'u0020]+$/g, ''); 
    }
    function getTextNodesIn(node, includeWhitespaceNodes) {
        var textNodes = [], whitespace = /^'s*$/;
        function getTextNodes(node) {
            if (node.nodeType == 3) {
                if (includeWhitespaceNodes || !whitespace.test(node.nodeValue)) {
                    textNodes.push(node);
                }
            } else {
                for (var i = 0, len = node.childNodes.length; i < len; ++i) {
                    getTextNodes(node.childNodes[i]);
                }
            }
        }
        getTextNodes(node);
        return textNodes;
    }
    function removeEmptyTextNodes() {
        var el, trimVal, nodeList = getTextNodesIn(document, true);
        if(Object.prototype.toString.call(nodeList) !== '[object Array]') { return; }
        while (el = nodeList.pop()) {
            trimVal = tirmSpecial(el.nodeValue);
            if (trimVal.length > 0) {
                el.nodeValue = trimVal;
                continue;
            }
            el.parentNode.removeChild(el);
        }
    }
    $("#remEmpty").click(removeEmptyTextNodes);
});

为了演示我所说的空文本节点的含义,请使用IE的开发人员窗口,并在使用javascript删除它们之前查看我的Fiddle中的代码。

现在我的解决方案到目前为止可以工作,我测试了它,但我讨厌我需要编辑整个 DOM 只是为了做到这一点,我还在网上发现了一些其他解决方案,有些甚至可能来自堆栈溢出,我只是不记得在哪里,但这些都是丑陋的黑客,就像我的一样,我将在下面发布它们。

请伙计们帮助我,那里必须有一个更好的解决方案。

有人建议将您的标记更改为此标记

<ul
    ><li
        ><label class="field">Some Stuff 1</label
        ><input class="value" type="text"
    /></li
    ><li
        ><label class="field">Some Stuff 2</label
        ><input class="value" type="text"
    /></li
    ><li
        ><label class="field">Some Stuff 3</label
        ><input class="value" type="text"
    /></li
    ><li
        ><label class="field">Some Stuff 4</label
        ><input class="value" type="text"
    /></li
    ><li
        ><label class="field">Some Stuff 5</label
        ><input class="value" type="text"
    /></li
    ><li
        ><label class="field">Some Stuff 6</label
        ><input class="value" type="text"
    /></li
    ><li
        ><label class="field">Some Stuff 7</label
        ><input class="value" type="text"
    /></li
    ><li
        ><label class="field">Some Stuff 8</label
        ><input class="value" type="text"
    /></li
></ul>

这有效,一段时间后变得可读,但这有一个缺点,团队中的每个人都记得做这个可怕的黑客,更不用说自动代码缩进完全被破坏意味着每个人的生产力都会降低。

另一个CSS黑客是执行以下操作

ul {
    list-style: none;
    width: 100%;
    /* Hack to keep "Text - Empty Text Node" elements from taking up space. */
    letter-spacing: -4px;
    word-spacing: -4px;
}
ul > li {
    display: inline-block;
    width: 25%;
    padding: 3px 8px;
    /* Reverse the Needed Hack to keep "Text - Empty Text Node" elements from taking up space. */
    letter-spacing: normal;
    word-spacing: normal;
}

这也有效,但您必须记住在使用它的任何地方应用和撤消字距技巧,如果您实际上在包含需要此技巧的其他内联块元素的部分中有文本,它也会产生一些问题,然后您必须添加一个<span></span>或其他包装元素,以便您可以逆转黑客。

这是一个相当简单的修复:

ul {
    list-style: none;
    margin-left: 4px;
    width: 100%;
}
ul > li {
    display: inline-block;
    margin-left: -4px;
    padding: 3px 8px;
    width: 25%;
}

不需要JavaScript!

编辑:

FWIW,我认为您最好接受这样一个事实,即浮动li元素是实现您想要的最简单方法:

ul {
    clear: both;
    list-style: none;
    width: 100%;
}
ul > li {
    float: left;
    padding: 3px 8px;
    width: 25%;
}

这适用于Chrome,FF,Safari,Opera和IE 8+。 它在IE 7中失败了,但我相信这是因为IE 7不支持框大小调整。

这里有一个非常好的跨浏览器解决方案,我在下面的代码中进行了改编:http://codepen.io/pageaffairs/pen/LkaIt

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style media="all">
html, body {
    width: 100%;
}
* {
    margin: 0;
    padding: 0;
    border: 0;
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
}
input {
    border: 1px solid;
}
ul {
    list-style: none;
    width: 100%;
}
ul > li {
    display: inline-block;
    width: 25%;
    padding: 3px 8px;
}
.field,
.value {
    display: block;
    width: 100%;
}
ul {
    display:table;/* Webkit Fix */
    width:100%;/* set width to stop FF from wrapping li's*/
    text-align:center; /* center list items*/   
    word-spacing:-.25em; /* hide whitespace nodes in all modern browsers (not for webkit)*/
    margin:0;
    padding:.25em 0;
    list-style:none;
}
ul li {
    display:-moz-inline-box; /* FF2 and K-Meleon */
    display:inline-block;
    vertical-align:bottom; 
    word-spacing:0; /* reset from parent ul*/
    /*margin:0 .25em; /*now you can set side margins without node conflict */
    padding:0 .5em;
}
* html ul li { display:inline;} /*IE6*/
*+html ul li { display:inline;} /*IE7*/
</style>
</head>
<body>
<ul>
    <li>
        <label class="field">
            Some Stuff 1
        </label>
        <input class="value" type="text" />
    </li>
    <li>
        <label class="field">Some Stuff 2</label>
        <input class="value" type="text" />
    </li>
    <li>
        <label class="field">Some Stuff 3</label>
        <input class="value" type="text" />
    </li>
    <li>
        <label class="field">Some Stuff 4</label>
        <input class="value" type="text" />
    </li>
    <li>
        <label class="field">Some Stuff 5</label>
        <input class="value" type="text" />
    </li>
    <li>
        <label class="field">Some Stuff 6</label>
        <input class="value" type="text" />
    </li>
    <li>
        <label class="field">Some Stuff 7</label>
        <input class="value" type="text" />
    </li>
    <li>
        <label class="field">Some Stuff 8</label>
        <input class="value" type="text" />
    </li>
</ul>
</body>
</html>