JavaScript追加列表项后按钮水平间距异常问题排查
问题解决:动态追加元素的按钮间距不一致问题
问题描述
HTML中原生的edit和delete按钮之间存在默认间距,但通过JavaScript追加结构、类名完全一致的列表项后,按钮间无间距,需要让追加的按钮与原生按钮显示效果一致。
代码示例
HTML
<ul id="list"> <li class="item"> <div>Milk</div> <button class="edit-button">edit</button> <button class="delete-button">X</button> </li> <li class="item"> <div>Cheerios</div> <button class="edit-button">edit</button> <button class="delete-button">X</button> </li> <li class="item"> <div>Cheese</div> <button class="edit-button">edit</button> <button class="delete-button">X</button> </li> </ul>
CSS
.title { text-align: center; } .main { border: 1px solid black; width: 50%; margin: auto; } #add-item { border: 1px solid black; } .input { width: 100%; padding: 12px 20px; margin: 8px 0; display: inline-block; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; } .label { margin: 10px; } #list { flex-direction: column; display: flex; } #list > li{ list-style-type: none; margin: 10px auto; } #list div { margin-bottom: 10px; text-align: center; } /* #list button { margin: 0px 4px; } */
JavaScript
// TODO: Grab the list const shoppingList = document.getElementById("list") // TODO: Grab the input field const titleInput = document.getElementById("title") // TODO: Grab the buttons. const submitButton = document.getElementById("submit-item") // const editButtons = document.querySelectorAll(".edit-button") // const delButtons = document.querySelectorAll(".delete-button") // TODO: Add click event to the button. submitButton.addEventListener('click', (e) => { e.preventDefault() addItem(titleInput.value) }); // TODO: Create a function to add item. // ! Requirement: Must have: // ! - item text // ! - edit button // ! - delete button // ! - input field // ! Requirement: Add event listeners to buttons // TODO: Edit button must get pass the Parent node. function addItem(title) { console.log("Running addItem") let newItem = document.createElement("li") newItem.classList.add("item") let newTitle = document.createElement("div") newTitle.textContent = title let editBtn = document.createElement("button") editBtn.classList.add("edit-button") editBtn.innerText = "edit" let delBtn = document.createElement("button") delBtn.classList.add("delete-button") delBtn.innerText = "X" editBtn.addEventListener('click', (e) => { console.log("Pressed the edit button", e) editItem(e.target.parentElement) }); newItem.appendChild(newTitle) newItem.appendChild(editBtn) newItem.appendChild(delBtn) shoppingList.appendChild(newItem) } // TODO: Create a function to edit item. // ! Requirement: Parent Node (Element) parameter // TODO: Remove the Edit, Delete buttons. // TODO: Grab text content of the div and assign to variable. // TODO: Add an input element to the parent node. // TODO: Set input's value to the variable with the text content. // TODO: Add the save button. function editItem(listItem) { let title; listItem.childNodes.forEach(element => { console.log(element.tagName) if(element.tagName === "DIV") { title = element.innerText } }); listItem.innerHTML = "" let editInput = document.createElement("input") editInput.value = title let saveBtn = document.createElement("button") saveBtn.classList.add("save-button") saveBtn.innerText = "save" listItem.append(editInput, saveBtn) saveBtn.addEventListener('click', (e) => { saveItem(e.target.parentNode) }); } // TODO: Create a function to save item. // ! Requirement: Parent Node (Element) parameter // TODO: Get the value of the input field and assign to variable // TODO: Remove the input and save button. // TODO: Create the div, edit, and delete buttons. // TODO: Set the div's text content to the variable with the input's value. // TODO: Add all those elements to the parent element. function saveItem(listItem) { let titleEdit; listItem.childNodes.forEach(element => { if(element.tagName === "INPUT") { titleEdit = element.value } }); listItem.innerHTML = ""; let changedTitle = document.createElement("div"); changedTitle.textContent = titleEdit; let editBtn = document.createElement("button"); editBtn.classList.add("edit-button"); editBtn.innerText = "edit"; let delBtn = document.createElement("button"); delBtn.classList.add("delete-button"); delBtn.innerText = "X"; listItem.appendChild(changedTitle); listItem.appendChild(editBtn); listItem.appendChild(delBtn); } // TODO: Create a function to delete item. // ! Requirement: Parent Node (Element) paramter // I believe you can figure this one out yourself. <3 :)
问题原因
原生HTML里的按钮之间存在换行或空格,浏览器会将这些空白字符解析为一个空格,从而产生按钮间的间距;而通过JavaScript的appendChild或append方法添加元素时,元素之间没有插入任何空白字符,所以按钮是紧密排列的,没有间距。
解决方案
方案1:用CSS统一设置按钮间距(推荐)
取消CSS中注释的按钮样式,或者添加新样式,直接给按钮设置margin,确保原生和动态添加的按钮有统一间距:
#list button { margin-left: 4px; }
也可以更精确地只给delete按钮设置左边距:
.delete-button { margin-left: 4px; }
方案2:动态添加元素时插入空白文本节点
在添加edit按钮和delete按钮之间,插入一个空白文本节点,模拟原生HTML中的空格:
// 在addItem函数中修改 newItem.appendChild(newTitle) newItem.appendChild(editBtn) // 插入空白文本节点 newItem.appendChild(document.createTextNode(' ')) newItem.appendChild(delBtn) shoppingList.appendChild(newItem)
同时在saveItem函数中也要做同样修改:
// 在saveItem函数中修改 listItem.appendChild(changedTitle); listItem.appendChild(editBtn); listItem.appendChild(document.createTextNode(' ')); listItem.appendChild(delBtn);
方案3:用Flex布局的gap属性控制间距
给列表项设置flex布局,用gap属性控制按钮间距,这是更现代的布局方式:
#list > li { list-style-type: none; margin: 10px auto; display: flex; flex-direction: column; align-items: center; } /* 给按钮容器设置行内flex和间距 */ #list > li div + div { display: flex; gap: 4px; }
注:此方案需要调整HTML结构,把两个按钮放在同一个div中,方便统一控制。
已尝试的解决方法
- 为按钮添加样式,但追加项按钮间距仍小于原生项,未解决
- 多浏览器测试,问题均存在,排除渲染差异
- 使用CSS重置,原生按钮仍有间距,追加项无,问题依旧
- 代码放入在线编辑器,问题复现,排除本地环境问题
- 检查代码换行/空格,无异常
- 咨询导师未找到原因,暂得临时方案但需根本解决
参考过的StackOverflow文章
- Why are initial CSS styles not visible on DOM element style field?(无关)
- Appended Element with Missing Styles(无关)
- Remove Blank Spaces Between Buttons in HTML CSS(无关)
- Can I use a DIV inside a List Item?(排除结构问题)
- Appended Content do not get the proper css effect(无关)
- Appending Elements to DOM with Indentation Spacing(无关)
- Strange Error When Appending Elements in JavaScript(无关)
内容的提问来源于stack exchange,提问作者DragonsTea25
相关产品推荐
相关产品推荐

