I am making a todo-list. And I added row striping on it, which worked.
Now when I delete a task it should update the row striping, which it does not. After deleting an element instead of being eg. white white beige it should update to white beige white.
I have a tried a lot of things but I couldn't get anything to work. To be fair I am new to all of this and not quite experienced in working with row striping.
Is there even a way to do it?
Here is what I have so far:
loadEvents();
function loadEvents() {
document.querySelector('form').addEventListener('submit', submit);
document.querySelector('ul').addEventListener('click', deleteOrTick);
}
function submit(a) {
a.preventDefault();
let input = document.querySelector('input');
if (input.value != '')
addTask(input.value);
input.value = '';
}
function addTask(task) {
let ul = document.querySelector('ul');
let li = document.createElement('li');
li.innerHTML = `<div class="input-group mb-3 row"><div class="col-11"><label>${task}</label></div>
<span class="delete">x</span></div>`;
ul.appendChild(li);
document.querySelector('.allToDos').style.display = 'block';
}
function deleteOrTick(a) {
if (a.target.className == 'delete')
deleteTask(a);
}
function deleteTask(a) {
let remove = a.target.parentNode;
let parentNode = remove.parentNode;
parentNode.removeChild(remove);
event.stopPropagation();
}
ul {
list-style-type: none;
}
li {
font-size: 1.3em;
color: #2f4f4f;
}
.todo li:nth-child(2n) {
background: #e0d9c3;
}
.todo {
width: 500px;
}
.delete {
cursor: pointer;
}
<div class="container">
<form action="index.html" method="post">
<div class="heading">
<h1 class="header">ToDo-List</h1>
<p class="intro">Do what you do</p>
</div>
<div class="input-group mb-3">
<input type="text" class="form-control" name="task" placeholder="Add a todo" aria-describedby="basic-addon2">
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="submit">Add</button>
</div>
</div>
</form>
</div>
<div class="container">
<div class="allToDos ">
<ul class="todo">
<li>
<div class="input-group mb-3 row">
<div class="col-11">
<label>hi was geht ab</label>
</div>
<span class="delete">x</span>
</div>
</li>
</ul>
</div>
</div>
deleteTask can be
function deleteTask(event) {
let remove = event.target.parentNode;
let parentNode = remove.parentNode;
remove.parentNode.remove();
event.stopPropagation();
}
document.querySelector('li').addEventListener('click',deleteOrTick); // not ul
function deleteTask(a){
let remove = a.currentTarget;
let parentNode = remove.parentNode;
parentNode.removeChild(remove);
a.stopPropagation(); //not event
}
Related
I'm using the clone method to duplicate a form. I'm adding and removing the active
class on the buttons but, once I clone the form, the duplicate buttons no longer
function because they share the same class as the original. I want the buttons to still
function regardless how many times I clone it. I used jQuery and JavaScript, and I'm
still new to programming. Can you please give me some ideas as to how to solve this.
Thanks in advance fellow developers.
Here is my HTML Code:
<div class="column-bottom phone">
<p class="para_txt">Phone</p>
<div id="main-wrapper">
<div id="wrapper_1" class="parentClass">
<div class="basic_infor">
<p>Select the nature of phone:</p>
<div class="parent_btns">
<button class="func_btns btn_first_4 " >Private</button>
<button class="func_btns btn_second_4" >Work</button>
</div>
</div>
<div class="basic_infor">
<p>Select the type of phone:</p>
<div class="parent_btns">
<button class="func_btns btn_5">Mobile</button>
<button class="func_btns btn_6 ">Telephone</button>
<button class="func_btns btn_7 ">Fax</button>
<button class="func_btns btn_8">Extension</button>
</div>
</div>
<div class="txt_area">
<input type="textarea" placeholder="+27 85 223 5258">
<span onclick="delete_el();">x</span>
</div>
</div>
</div>
<div class="btn_add">
<button class="repl_btns phone_repl" onclick="duplicate();">Add additional</button>
<p>Display on foreman contact list?</p>
<input type="checkbox" id="input_field" name="Phone_contact">
</div>
</div>
Here is my jQuery and JavaScript Code. I selected the class for the first button and
added a active class to it while removing the active class for the second button. I did
the same for the rest of the buttons.
//private btn
$(".btn_first_4").click(function () {
$(this).addClass("is_active");
$(".btn_second_4").removeClass("is_active");
});
//work btn
$(".btn_second_4").click(function () {
$(this).addClass("is_active");
$(".btn_first_4").removeClass("is_active");
});
//Bottom 5 btns
$(".btn_5").click(function () {
$(this).addClass("is_active");
$(".btn_6,.btn_7,.btn_8").removeClass("is_active");
})
$(".btn_6").click(function () {
$(this).addClass("is_active");
$(".btn_5,.btn_7,.btn_8").removeClass("is_active");
})
$(".btn_7").click(function () {
$(this).addClass("is_active");
$(".btn_5,.btn_6,.btn_8").removeClass("is_active");
})
$(".btn_8").click(function () {
$(this).addClass("is_active");
$(".btn_5,.btn_6,.btn_7").removeClass("is_active");
})
/*
Cloning Functions....
I tried to set the id of my new clone to "wrapper_2", but it only works when i clone it
once. I wanted to change the class attribute this way but I realize it wont work as
well. Please advise. Thanks
*/
function duplicate(){
const wrapper = document.getElementById("wrapper_1");
const clone = wrapper.cloneNode(true);
clone.id = "wrapper_2";
const main_wrapper = document.getElementById("main-wrapper");
main_wrapper.appendChild(clone)
}
function delete_el() {
const del_el = document.getElementById("wrapper_2");
del_el.remove();
}
Problems
If you use .cloneNode() any event handlers bound to the original will not carry over to the clone. Fortunately you are using jQuery which has it's own method .clone(). It has the ability to clone and keep event handlers, $(selector).clone(true) to copy with events and $(selector).clone(true, true) for a deep copy with events.
Note: Using .clone() has the side-effect of producing elements with duplicate id attributes, which are supposed to be unique. Where possible, it is recommended to avoid cloning elements with this attribute or using class attributes as identifiers instead.
.clone()|jQuery API Documentation
Do not clone anything with an id, in fact you are using jQuery so don't use id at all. Convert every id to a class, it might feel like a lot of work but in the long run you'll be thankful you did.
Do not use inline event handlers
<button onclick="lame(this)">DON'T DO THIS</button>
This is especially important if you use jQuery which makes event handling incredibly easy to write and very versatile.
let count = 0;
$('output').val(++count);
$('.remove').hide();
$('.select button').on('click', function() {
const $old = $(this).parent().find('.active');
if (!$old.is(this)) {
$old.removeClass('active');
}
$(this).toggleClass('active');
});
$('.clear').on('click', function() {
$(this).parent().find('input').val('');
});
$('.remove').on('click', function() {
$(this).closest('.fields').remove();
let out = $.makeArray($('output'));
count = out.reduce((sum, cur, idx) => {
cur.value = idx + 1;
sum = idx + 1;
return sum;
}, 0);
});
$('.add').on('click', function() {
const $first = $('.fields').first();
const $copy = $first.clone(true, true);
$copy.insertAfter($('.fields').last());
$copy.find('output').val(++count);
$copy.find('.remove').show();
$copy.find('input').val('');
});
html {
font: 300 2ch/1.2 'Segoe UI'
}
fieldset {
min-width: fit-content
}
.fields {
margin-top: 1rem;
}
output {
font-weight: 900;
}
menu {
display: flex;
align-items: center;
margin: 0.5rem 0 0.25rem;
}
button,
input {
display: inline-block;
font: inherit;
font-size: 100%;
}
button {
cursor: pointer;
border: 1.5px ridge lightgrey;
}
.numbers {
display: flex;
align-items: center;
margin: 1rem 0 0.5rem -40px;
}
.clear {
border: 0;
font-size: 1.25rem;
line-height: 1.25;
}
.right {
justify-content: flex-end;
}
.left {
padding-left: 0;
}
.number-3 {
width: 9rem;
}
.number-1 {
width: 3rem;
}
[class^="number-"] {
font-family: Consolas
}
.clear {
border: 0;
background: transparent;
}
label+label {
margin-left: 6px;
}
button:first-of-type {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
button:nth-of-type(2) {
border-radius: 0;
}
button:last-of-type {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
.active {
outline: 2px lightblue solid;
outline-offset: -2px;
}
#foreman {
transform: translate(0, 1.5px)
}
.btn.remove {
display: block;
border-radius: 4px;
float: right;
}
<form id='phone'>
<fieldset class='main'>
<legend>Add Phone Numbers</legend>
<section class='fields'>
<fieldset>
<legend>Phone Number <output value='1'></output></legend>
<button class='btn remove' type='button'>Remove</button>
<label>Phone number is used for:</label>
<menu class='purpose select'>
<button class="btn priv" type='button'>Private</button>
<button class="btn work" type='button'>Work</button>
</menu>
<label>Select the type of phone:</label>
<menu class='type select'>
<button class="btn mob" type='button'>Mobile</button>
<button class="btn tel" type='button'>Telephone</button>
<button class="btn fax" type='button'>Fax</button>
</menu>
<menu class='numbers'>
<form name='numbers'>
<label>Number:  </label>
<input name='phone' class='number-3' type="tel" placeholder="+27 85 223 5258" required>
<label>  Ext.  </label>
<input name='ext' class='number-1' type='number' placeholder='327'>
<button class='btn clear' type='button'>X</button>
</form>
</menu>
</fieldset>
</section>
<fieldset>
<menu class='right'>
<button class='btn cancel' type='button'>Cancel</button>
<button class='btn done'>Done</button>
<button class='btn add' type='button'>Add</button>
</menu>
</fieldset>
<footer>
<menu>
<input id='foreman' name="contact" type="checkbox">
<label for='foreman'>Display on foreman contact list?</label>
</menu>
</footer>
</fieldset>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
When load page , JS add event click for elements ( elements were created)
When you clone new elements ( those do not add event click) and event click of you not working on those elements
You are using Jquery then i suggest you code same as below :
$(document).on('click', ".btn_first_4", function () {
$(this).addClass("is_active");
$(".btn_second_4").removeClass("is_active");
});
//work btn
$(document).on('click', ".btn_second_4", function () {
$(this).addClass("is_active");
$(".btn_first_4").removeClass("is_active");
});
//Bottom 5 btns
$(document).on('click', ".btn_5", function () {
$(this).addClass("is_active");
$(".btn_6,.btn_7,.btn_8").removeClass("is_active");
})
$(document).on('click', ".btn_6", function () {
$(this).addClass("is_active");
$(".btn_5,.btn_7,.btn_8").removeClass("is_active");
})
$(document).on('click', ".btn_7", function () {
$(this).addClass("is_active");
$(".btn_5,.btn_6,.btn_8").removeClass("is_active");
})
$(document).on('click', ".btn_8", function () {
$(this).addClass("is_active");
$(".btn_5,.btn_6,.btn_7").removeClass("is_active");
})
function duplicate(){
const wrapper = document.getElementById("wrapper_1");
const clone = wrapper.cloneNode(true);
clone.id = "wrapper_2";
const main_wrapper = document.getElementById("main-wrapper");
main_wrapper.appendChild(clone)
}
function delete_el() {
const del_el = document.getElementById("wrapper_2");
del_el.remove();
}
.is_active {
background-color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="column-bottom phone">
<p class="para_txt">Phone</p>
<div id="main-wrapper">
<div id="wrapper_1" class="parentClass">
<div class="basic_infor">
<p>Select the nature of phone:</p>
<div class="parent_btns">
<button class="func_btns btn_first_4 " >Private</button>
<button class="func_btns btn_second_4" >Work</button>
</div>
</div>
<div class="basic_infor">
<p>Select the type of phone:</p>
<div class="parent_btns">
<button class="func_btns btn_5">Mobile</button>
<button class="func_btns btn_6 ">Telephone</button>
<button class="func_btns btn_7 ">Fax</button>
<button class="func_btns btn_8">Extension</button>
</div>
</div>
<div class="txt_area">
<input type="textarea" placeholder="+27 85 223 5258">
<span onclick="delete_el();">x</span>
</div>
</div>
</div>
<div class="btn_add">
<button class="repl_btns phone_repl" onclick="duplicate();">Add additional</button>
<p>Display on foreman contact list?</p>
<input type="checkbox" id="input_field" name="Phone_contact">
</div>
</div>
Goal
Avoid unnecessary event bindings.
Sample code
Comment box with a reply button for each individual comment
const btns = document.getElementsByClassName('reply-btn');
for (let i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', showCommentContentAsPreview);
}
function showCommentContentAsPreview(e) {
console.log('showCommentContentAsPreview()');
// CHECK IF THIS BUTTON ALREADY BINDED !!!
const previewDiv = document.getElementById('preview');
const commentId = e.target.getAttribute('data-comment-id')
const commentDiv = document.getElementById('comment-' + commentId);
const commentText = commentDiv.querySelector('p').innerText
const closeReplyBtn = previewDiv.querySelector('button');
const previewContent = previewDiv.querySelector('.preview-content');
// set to preview
previewContent.innerText = commentText;
// show reply close button
closeReplyBtn.classList.remove('hidden');
// bind EventListener to "reply close button"
closeReplyBtn.addEventListener('click', closeReply)
function closeReply() {
console.log('bind to btn');
previewContent.innerText = '';
this.removeEventListener('click', closeReply);
closeReplyBtn.classList.add('hidden');
}
}
.hidden {
display: none;
}
.comment {
border-bottom: 1px solid #000;
padding: 5px;
}
.preview {
background-color: #ccc;
padding: 20px;
margin-top: 20px;
}
<div>
<!-- comment list -->
<div id="comment-1" class="comment">
<p>Comment Content 1</p>
<button class="reply-btn" data-comment-id="1">reply</button>
</div>
<div id="comment-2" class="comment">
<p>Comment Content 2</p>
<button class="reply-btn" data-comment-id="2">reply</button>
</div>
</div>
<!-- output -->
<div>
<div id="preview" class="preview">
<div class="preview-content"></div>
<button class="hidden">Close Preview</button>
</div>
</div>
Simulate problem
When you try the example, the following two scenarios occur:
Click reply once and then click "close preview"
Click on reply several times and then on "close preview".
Question
How can I avoid multiple bindings to the same button? I am already thinking about singleton.
Instead of binding a listener to every element in the series, you can bind a single listener once on a common parent of them all, and then use element.matches() to determine if the click target is the one that you want before doing more work. See the following example:
function logTextContent (elm) {
console.log(elm.textContent);
}
function handleClick (ev) {
if (ev.target.matches('.item')) {
logTextContent(ev.target);
}
}
document.querySelector('ul.list').addEventListener('click', handleClick);
<ul class="list">
<li class="item">Item 1</li>
<li class="item">Item 2</li>
<li class="item">Item 3</li>
<li class="item">Item 4</li>
<li class="item">Item 5</li>
</ul>
With the helpful hints from #Zephyr and #jsejcksn I have rewritten the code of the above question. Thus I have achieved my goal of avoiding multiple identical bindings to one element.
const container = document.getElementById('comment-container');
const previewDiv = document.getElementById('preview');
const closeReplyBtn = previewDiv.querySelector('button');
const previewContent = previewDiv.querySelector('.preview-content');
container.addEventListener('click', handleClick);
function handleClick(ev) {
if (ev.target.matches('.reply-btn')) {
if (ev.target.getAttribute('listener') !== 'true') {
removeOtherListenerFlags();
ev.target.setAttribute('listener', 'true');
showCommentContentAsPreview(ev);
}
}
if (ev.target.matches('#preview button')) {
previewContent.innerText = '';
closeReplyBtn.classList.add('hidden');
removeOtherListenerFlags();
}
}
function showCommentContentAsPreview(e) {
console.log('showCommentContentAsPreview()');
const commentId = e.target.getAttribute('data-comment-id')
const commentDiv = document.getElementById('comment-' + commentId);
const commentText = commentDiv.querySelector('p').innerText
// set to preview
previewContent.innerText = commentText;
// show reply close button
closeReplyBtn.classList.remove('hidden');
}
function removeOtherListenerFlags() {
const replyBtns = container.querySelectorAll('.reply-btn')
Object.keys(replyBtns).forEach((el) => {
replyBtns[el].removeAttribute('listener');
})
}
.hidden {
display: none;
}
.comment {
border-bottom: 1px solid #000;
padding: 5px;
}
.preview {
background-color: #ccc;
padding: 20px;
margin-top: 20px;
}
<div id="comment-container">
<div id="comment-listing">
<!-- comment list -->
<div id="comment-1" class="comment">
<p>Comment Content 1</p>
<button class="reply-btn" data-comment-id="1">reply 1</button>
</div>
<div id="comment-2" class="comment">
<p>Comment Content 2</p>
<button class="reply-btn" data-comment-id="2">reply 2</button>
</div>
</div>
<!-- output -->
<div>
<div id="preview" class="preview">
<div class="preview-content"></div>
<button class="hidden">Close Preview</button>
</div>
</div>
</div>
Cool and Thanks!
I have a HTML code and I want to select each label that has the data-anwser set to "correct", how can I do it with pure Javascript? Here's my HTML
<div class="form-group answer" role="group">
<label data-answer="correct">
<div>Test</div>
</label>
</div>
<div class="form-group answer" role="group">
<label data-answer="incorrect">
<div>Test</div>
</label>
</div>
And here's the begining of what I tried with the JS
<script>
var div = document.getElementsByClassName('answer');
window.onload = (event) => {
for (var i = 0; i < div.length; i++) {
var label = div[i].getElementsByTagName('label');
if (label[i].dataset == 'correct') {
console.log("CORRECTE");
} else {
console.log("INCORECTE");
}
console.log(label[i].dataset);
}
}
</script>
Thank you:)
use query selectors:
const btn = document.getElementById('btn');
btn.addEventListener('click', ev => {
const correctAnswers = document.querySelectorAll('.answer [data-answer="correct"]');
correctAnswers.forEach(el => {
el.classList.toggle('selected');
});
});
.form-group > label {
margin: 0.33em;
padding: 0.33em;
border: 2px solid gray;
background-color: silver;
width: 250px;
display: inline-block;
}
.form-group > label.selected {
background-color: yellow;
border: 2px dotted black;
}
<div class="form-group answer" role="group">
<label data-answer="correct">
<div>Test 1</div>
</label>
</div>
<div class="form-group answer" role="group">
<label data-answer="incorrect">
<div>Test 2</div>
</label>
</div>
<div class="form-group answer" role="group">
<label data-answer="correct">
<div>Test 3</div>
</label>
</div>
<button id="btn">highlight correct answers</button>
Try this
var answersCorrect = document.querySelectorAll("*[data-answer='correct']")
Array.from(answersCorrect).forEach(element => {
console.log(element)
})
I am polluting a div with the help of following codes every time the 'Add' button is clicked on the website.
const addClick = document.getElementById("addBtn");
addClick.addEventListener("click", () => {
addNote();
});
function addNote(){
const notes = document.getElementById("notes");
notes.innerHTML += `
<div class="note">
<div class="delete"><i class="fas fa-times"></i></div>
<button class="save"><i class="fas fa-check"></i></button>
<textarea type="text" class="text"></textarea>
</div>
`;
}
With this code, I am generating note(s) that has delete or save options. I want to know which class element was clicked. I want to do this with vanilla Javascript.
You can use event delegation:
add an event listener to the parent element or document and then check if the clicked element contains the class that you want to target.
You may need to also check if the element clicked is a child of the button (the icon)
document.addEventListener("click", function(event) {
var target = event.target;
if (isElement(target, 'delete')) {
console.log("delete button clicked", target.closest(".note"));
} else if (isElement(target, "save")) {
console.log("save button clicked", target.closest(".note"));
}
});
function isElement(element, className) {
return element.classList.contains(className) || element.closest(`.${className}`);
}
<div class="note">
<button class="delete"><i class="fas fa-times"></i>Delete</button>
<button class="save"><i class="fas fa-check"></i>Save</button>
<textarea type="text" class="text"></textarea>
</div>
<div class="note">
<button class="delete"><i class="fas fa-times"></i>Delete</button>
<button class="save"><i class="fas fa-check"></i>Save</button>
<textarea type="text" class="text"></textarea>
</div>
<div class="note">
<button class="delete"><i class="fas fa-times"></i>Delete</button>
<button class="save"><i class="fas fa-check"></i>Save</button>
<textarea type="text" class="text"></textarea>
</div>
Here is a snippet for your case.
HTML
<div id="app"></div>
JS
const appDiv = document.getElementById("app");
appDiv.innerHTML = `<h1>JS Starter</h1>`;
[1, 2, 3, 4].forEach((el, index) => {
const note = document.createElement("div");
appDiv.insertAdjacentElement("afterend", note);
note.classList.add("note");
note.addEventListener("click", () => {
appDiv.innerHTML = `<h1>Box number: ${index + 1}<h1>`;
});
});
CSS
.note {
border: 1px solid black;
height: 100px;
width: 100px;
margin: 10px;
}
.note:hover {
cursor: pointer;
}
I am trying to step away from jsTree as this is not as much as configurable as having my own custom code. I am making use of Bootstrap to have a somewhat similar functionality as jsTree. I am also stepping away from jQuery (for now), because of debugging reasons.
//Event delegation
function BindEvent(parent, eventType, ele, func) {
var element = document.querySelector(parent);
element.addEventListener(eventType, function(event) {
var possibleTargets = element.querySelectorAll(ele);
var target = event.target;
for (var i = 0, l = possibleTargets.length; i < l; i++) {
var el = target;
var p = possibleTargets[i];
while (el && el !== element) {
if (el === p) {
return func.call(p, event);
}
el = el.parentNode;
}
}
});
}
//Add content after referenced element
function insertAfter(referenceNode, newNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
//Custom function
function LoadSubOptions(ele) {
ele = ele.parentElement.parentElement;
let newEle = document.createElement("div");
newEle.classList.add("row", "flex");
//Generated HTML Content (currently hard coded):
newEle.innerHTML = "<div class='col-xs-1'><div class='tree-border'></div></div><div class='col-xs-11'><div class='row'><div class='col-xs-12'><button class='btn btn-default btn-block btn-lg'>Test</button></div></div></div>";
insertAfter(ele, newEle);
}
//Bind method(s) on button click(s)
BindEvent("#tree-replacement", "click", "button", function(e) {
LoadSubOptions(this);
});
#tree-replacement button {
margin-top: 5px;
}
.tree-border {
border-left: 1px dashed #000;
height: 100%;
margin-left: 15px;
}
.flex {
display: flex;
}
/*Probably not wise to use this method on Bootstrap's grid system: */
#tree-replacement .row.flex>[class*='col-'] {
display: flex;
flex-direction: column;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet" />
<div class="container">
<div id="tree-replacement">
<div class="row">
<div class="col-xs-12">
<button class="btn btn-default btn-block btn-lg">
Option 1
</button>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<button class="btn btn-default btn-block btn-lg">
Option 2
</button>
</div>
</div>
<!--The generated html as example: -->
<!--<div class="row">
<div class="col-xs-1">
<div class="tree-border">
</div>
</div>
<div class="col-xs-11">
<div class="row">
<div class="col-xs-12">
<button class="btn btn-default btn-block btn-lg">
Option 2
</button>
</div>
</div>
</div>
</div>-->
</div>
</div>
JSFiddle
I added a border in a .column-*-1 to allow for some spacing for the border:
The spacing however, I find a bit too much. How could I address this problem? I would like to refrain from styling Bootstrap's grid system (meaning I preferably would not want to touch any styling behind .col-* and .row classes etc.) because this might break the responsiveness or anything else related to Bootstrap.
Edit:
I also noticed that when adding a lot of buttons by just clicking them, the layout of tree will start failing as well. (I am aware this is a different question, so if I need to post another question regarding this problem, please do let me know) Is there a way I could address this so that the element works correctly?
Add this little CSS
#tree-replacement .row.flex > .col-xs-11:nth-child(2):before {
content: ' ';
position: absolute;
left: calc(-100% / 11 + 30px);
top: 2em;
border-top: 1px dashed #000000;
width: calc(100% / 5 - 15px);
}
//Event delegation
function BindEvent(parent, eventType, ele, func) {
var element = document.querySelector(parent);
element.addEventListener(eventType, function(event) {
var possibleTargets = element.querySelectorAll(ele);
var target = event.target;
for (var i = 0, l = possibleTargets.length; i < l; i++) {
var el = target;
var p = possibleTargets[i];
while (el && el !== element) {
if (el === p) {
return func.call(p, event);
}
el = el.parentNode;
}
}
});
}
//Add content after referenced element
function insertAfter(referenceNode, newNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
//Custom function
function LoadSubOptions(ele) {
ele = ele.parentElement.parentElement;
let newEle = document.createElement("div");
newEle.classList.add("row", "flex");
//Generated HTML Content (currently hard coded):
newEle.innerHTML = "<div class='col-xs-1'><div class='tree-border'></div></div><div class='col-xs-11'><div class='row'><div class='col-xs-12'><button class='btn btn-default btn-block btn-lg'>Test</button></div></div></div>";
insertAfter(ele, newEle);
}
//Bind method(s) on button click(s)
BindEvent("#tree-replacement", "click", "button", function(e) {
LoadSubOptions(this);
});
#tree-replacement button {
margin-top: 5px;
}
.tree-border {
border-left: 1px dashed #000;
height: 100%;
margin-left: 15px;
}
.flex {
display: flex;
}
/*Probably not wise to use this method on Bootstrap's grid system: */
#tree-replacement .row.flex>[class*='col-'] {
display: flex;
flex-direction: column;
}
#tree-replacement .row.flex > .col-xs-11:nth-child(2):before {
content: ' ';
position: absolute;
left: calc(-100% / 11 + 30px);
top: 2em;
border-top: 1px dashed #000000;
width: calc(100% / 5 - 15px);
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet" />
<div class="container">
<div id="tree-replacement">
<div class="row">
<div class="col-xs-12">
<button class="btn btn-default btn-block btn-lg">
Option 1
</button>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<button class="btn btn-default btn-block btn-lg">
Option 2
</button>
</div>
</div>
<!--The generated html as example: -->
<!--<div class="row">
<div class="col-xs-1">
<div class="tree-border">
</div>
</div>
<div class="col-xs-11">
<div class="row">
<div class="col-xs-12">
<button class="btn btn-default btn-block btn-lg">
Option 2
</button>
</div>
</div>
</div>
</div>-->
</div>
</div>
Here I have used absolute positioning and increased height by 5px which kind of makes it touches the next div element.
Here is the Fiddle Link
and the Code Snippet:
//Event delegation
function BindEvent(parent, eventType, ele, func) {
var element = document.querySelector(parent);
element.addEventListener(eventType, function(event) {
var possibleTargets = element.querySelectorAll(ele);
var target = event.target;
for (var i = 0, l = possibleTargets.length; i < l; i++) {
var el = target;
var p = possibleTargets[i];
while (el && el !== element) {
if (el === p) {
return func.call(p, event);
}
el = el.parentNode;
}
}
});
}
//Add content after referenced element
function insertAfter(referenceNode, newNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
//Custom function
function LoadSubOptions(ele) {
ele = ele.parentElement.parentElement;
let newEle = document.createElement("div");
newEle.classList.add("row", "flex");
//Generated HTML Content (currently hard coded):
newEle.innerHTML = "<div class='col-xs-1'><div class='tree-border'></div></div><div class='col-xs-11'><div class='row'><div class='col-xs-12'><button class='btn btn-default btn-block btn-lg'>Test</button></div></div></div>";
insertAfter(ele, newEle);
}
//Bind method(s) on button click(s)
BindEvent("#tree-replacement", "click", "button", function(e) {
LoadSubOptions(this);
});
#tree-replacement button {
margin-top: 5px;
}
.tree-border {
border-left: 1px dashed #000;
height: calc(100% + 5px);
margin-left: 20px;
position: absolute;
}
.flex {
position: relative;
display: flex;
}
.col-xs-11 .col-xs-12 {
padding-left: 0;
}
/*Probably not wise to use this method on Bootstrap's grid system: */
#tree-replacement .row.flex>[class*='col-'] {
display: flex;
flex-direction: column;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"/>
<div class="container">
<div id="tree-replacement">
<div class="row">
<div class="col-xs-12">
<button class="btn btn-default btn-block btn-lg">
Option 1
</button>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<button class="btn btn-default btn-block btn-lg">
Option 2
</button>
</div>
</div>
<!--<div class="row">
<div class="col-xs-1">
<div class="tree-border">
</div>
</div>
<div class="col-xs-11">
<div class="row">
<div class="col-xs-12">
<button class="btn btn-default btn-block btn-lg">
Option 2
</button>
</div>
</div>
</div>
</div>-->
</div>
</div>