Count each individual vowel in string upon click - javascript

Writing some js for an html file where i input a sentence (string). and when i click a button, it outputs the amount of each individual vowel, excluding y and not paying attention to punctuation. I cannot use var so i am trying to make this work using let. I believe i'm on the right path here,starting with the vowel a, yet if the sentence doesn't contain an a i get an error. I can't think of what to do next. Any thoughts?
'use strict';
let vButton = document.querySelectorAll('#vowels');
vButton.forEach(function(blip) {
blip.addEventListener('click', function(evt) {
evt.preventDefault();
console.log('click');
let vowelString = document.getElementById('roboInput'),
sentence = vowelString.value;
if (sentence !== '') {
let aMatches = sentence.match(/a/gi).length;
alert("a - " + aMatches);
}
vowelString.value = '';
});
});
a {
cursor: pointer;
}
.well-robot {
min-height: 340px;
}
.input-robot {
width: 100%;
min-height: 100px;
}
.output-robot {
border: 1px solid #000000;
min-height: 150px;
margin-top: 10px;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css">
<div class="container">
<div class="alert alert-info">
Hello! I'm a smart robot. I can do many interesting things. Type something below and click a button to watch me work!
</div>
<div class="row">
<div class="col-sm-4">
<img src="./robot.gif">
</div>
<div class="col-sm-8 well well-robot">
<textarea id="roboInput" placeholder="Input something here!" class="input-robot"></textarea>
<div class="btn-group btn-group-justified">
<a class="btn btn-default" id="vowels">Count Vowels</a>
<a class="btn btn-default" id="anagrams">Count Anagrams</a>
<a class="btn btn-default" id="distance">Word Distance</a>
</div>
<div id="robotResult" class="output-robot">
</div>
</div>
</div>
</div>

When there's no match for the regular expression, .match() returns null, not an empty array, so you can't get the length. You need to check for that.
let matches = sentence.match(/a/gi);
let matchLength = matches ? matches.length : 0;
alert('a - ' + matchLength);

If I understand your question correctly, you may want something like this:
'use strict';
let vButton = document.querySelectorAll('#vowels');
vButton.forEach(function(blip) {
blip.addEventListener('click', function(evt) {
evt.preventDefault();
//console.log('click');
let vowelString = document.getElementById('roboInput'),
sentence = vowelString.value;
if (sentence) {
let result = {a: 0, e: 0, i: 0, o: 0, u: 0 };
for(var i = 0, l = sentence.length; i < l; i++) {
if(result.hasOwnProperty(sentence[i]))
result[sentence[i]]++;
}
console.log(result);
}
vowelString.value = '';
});
});
a {
cursor: pointer;
}
.well-robot {
min-height: 340px;
}
.input-robot {
width: 100%;
min-height: 100px;
}
.output-robot {
border: 1px solid #000000;
min-height: 150px;
margin-top: 10px;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css">
<div class="container">
<div class="alert alert-info">
Hello! I'm a smart robot. I can do many interesting things. Type something below and click a button to watch me work!
</div>
<div class="row">
<div class="col-sm-4">
<img src="./robot.gif">
</div>
<div class="col-sm-8 well well-robot">
<textarea id="roboInput" placeholder="Input something here!" class="input-robot"></textarea>
<div class="btn-group btn-group-justified">
<a class="btn btn-default" id="vowels">Count Vowels</a>
<a class="btn btn-default" id="anagrams">Count Anagrams</a>
<a class="btn btn-default" id="distance">Word Distance</a>
</div>
<div id="robotResult" class="output-robot">
</div>
</div>
</div>
</div>

Related

How can i get these Javascript functions to increment correctly without combining with my original variable?

I'm currently doing a Javascript challenge on Scrimba that requires you to recreate a Basketball scoreboard. I've gotten the design down but i'm having trouble with increment buttons to add either 1,2, or 3 points to either teams score. Each team's scoreboard has 3 buttons underneath that can add 1,2, or 3 points. Originally i was just going to write 6 functions, 3 for each team that would function based on which increment button you select for which team. I figured i could probably just write the three separate increment functions and find a way to pass in an argument to direct which team was getting the points. This worked except that the functions all target a 'points' variable so they end up incrementing off of each other when you add points to the opposite team.
Here is the HTML
<div class="container">
<div class="column">
<h3 class="title">HOME</h3>
<h2 class="score" id="home-score">0</h2>
<div>
<button class="increment-btn" onclick="add1Point('home-score')">+1</button>
<button class="increment-btn" onclick="add2Points('home-score')">+2</button>
<button class="increment-btn" onclick="add3Points('home-score')">+3</button>
</div>
</div>
<div class="column">
<h3 class="title">GUEST</h3>
<h2 class="score" id="guest-score">0</h2>
<div>
<button class="increment-btn" onclick="add1Point('guest-score')">+1</button>
<button class="increment-btn" onclick="add2Points('guest-score')">+2</button>
<button class="increment-btn" onclick="add3Points('guest-score')">+3</button>
</div>
And here is the JS
let points = 0
function add1Point(idValue){
let teamId = document.getElementById(idValue)
points += 1
teamId.textContent = points
}
function add2Points(idValue){
let teamId = document.getElementById(idValue)
points += 2
teamId.textContent = points
}
function add3Points(idValue){
let teamId = document.getElementById(idValue)
points += 3
teamId.textContent = points
}
I know i need to find a way to have two separate point variables for each team but I'm not sure how i can point the individual functions to a specific variable base on which teams button is selected. Not without creating a whole new function specifically for that variable. If possible i would like a solution with the most basic vanilla JS possible, I know there are more complex ways to solve this but im only so far with my learning. Thanks in advance!
use closures
function score(points = 0) {
return function(value) {
points += value;
return points;
}
}
const $homeScore = document.getElementById("home-score");
const $guestScore = document.getElementById("guest-score");
const homeScore = score();
const guestScore = score();
const $homeButtons = document.querySelectorAll("#home-buttons button");
const $guestButtons = document.querySelectorAll("#guest-buttons button");
for(let i = 0; i < $homeButtons.length; i++) {
$homeButtons[i].addEventListener("click", () => {
$homeScore.innerText = homeScore(i + 1);
});
}
for(let i = 0; i < $guestButtons.length; i++) {
$guestButtons[i].addEventListener("click", () => {
$guestScore.innerText = guestScore(i + 1);
});
}
.container {
display: flex;
justify-content: space-between;
background-color: black;
color: white;
font-family: Courier, Courier New, monospace;
padding: 2px 5px;
}
.container .column .score {
border: 1px solid white;
border-radius: 2px;
padding: 2px 5px;
text-align: center;
}
<div class="container">
<div class="column">
<h3 class="title">HOME</h3>
<h2 class="score" id="home-score">0</h2>
<div id="home-buttons">
<button class="increment-btn">+1</button>
<button class="increment-btn">+2</button>
<button class="increment-btn">+3</button>
</div>
</div>
<div class="column">
<h3 class="title">GUEST</h3>
<h2 class="score" id="guest-score">0</h2>
<div id="guest-buttons">
<button class="increment-btn">+1</button>
<button class="increment-btn">+2</button>
<button class="increment-btn">+3</button>
</div>
</div>
</div>
As far as I understand, you'd probably need 2 individual variables to hold the value for each team, here's an example for add 1 point
let guest = 0;
let home = 0;
function add1Point(idValue){
let teamId = document.getElementById(idValue)
if (idValue === 'guest-score') { //Assume your element has name to tell them apart
guest += 1;
teamId.textContent = guest;
} else {
home += 1;
teamId.textContent = home;
}
}
In the other hand, you should make your method reusable and flexible a little like this
function addPoints(idValue, point) {
let teamId = document.getElementById(idValue)
if (idValue === 'guest-score') { //Assume your element has name to tell them apart
guest += point;
teamId.textContent = guest;
} else {
home += point;
teamId.textContent = home;
}
}
then your code will look cleaner
<button class="increment-btn" onclick="addPoints('guest-score', 1)">+1</button>
<button class="increment-btn" onclick="addPoints('guest-score', 2)">+2</button>
<button class="increment-btn" onclick="addPoints('guest-score', 3)">+3</button>
You can simplify it to one function only:
var homeScore = 0;
var guestScore = 0;
var homeScoreEl = document.getElementById('home-score');
var guestScoreEl = document.getElementById('guest-score');
function addPoints(isHome, points = 1) {
window[isHome ? 'homeScore' : 'guestScore'] += points
window[isHome ? 'homeScoreEl' : 'guestScoreEl'].textContent = window[isHome ? 'homeScore' : 'guestScore']
}
.container {
display: flex;
justify-content: space-between;
background-color: black;
color: white;
font-family: Courier, Courier New, monospace;
padding: 2px 5px;
}
.container .column .score {
border: 1px solid white;
border-radius: 2px;
padding: 2px 5px;
text-align: center;
}
<div class="container">
<div class="column">
<h3 class="title">HOME</h3>
<h2 class="score" id="home-score">0</h2>
<div>
<button class="increment-btn" onclick="addPoints(true)">+1</button>
<button class="increment-btn" onclick="addPoints(true, 2)">+2</button>
<button class="increment-btn" onclick="addPoints(true, 3)">+3</button>
</div>
</div>
<div class="column">
<h3 class="title">GUEST</h3>
<h2 class="score" id="guest-score">0</h2>
<div>
<button class="increment-btn" onclick="addPoints(false)">+1</button>
<button class="increment-btn" onclick="addPoints(false, 2)">+2</button>
<button class="increment-btn" onclick="addPoints(false, 3)">+3</button>
</div>

My active/disable Functionality no longer works after cloning

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:&ThickSpace;</label>
<input name='phone' class='number-3' type="tel" placeholder="+27 85 223 5258" required>
<label>&ThickSpace;Ext.&ThickSpace;</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>

split textarea value properly jQuery regex

I am having problems to properly split textarea value. My current snippet split each line that starts with "-" and displays it as value of span element, but, it wont collect next line value which does not start with "-".
For example if I paste this text into textarea:
- first match
rest of first match
- second match
- third match
Script should output:
<span style="color:red;">- first match rest of first match </span><br>
<span style="color:red;">- second match</span><br>
<span style="color:red;">- third match</span><br>
$(document).ready(function() {
const regex = /^\s*-\s*/;
$("#txt").keyup(function() {
const entered = $('#textarea').val()
const lines = entered.split(/\n/);
let spans = "";
for (const line of lines) {
if (regex.test(line)) {
spans += "<span style='color:red;'>- " + line.replace(regex, '') + "</span><br/>";
}
}
$(".results").html(spans);
});
});
.row {
background: #f8f9fa;
margin-top: 20px;
padding: 10px;
}
.col {
border: solid 1px #6c757d;
}
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<div class="container">
<div class="row">
<div class="col-12">
<form>
<textarea id="textarea" rows="5" cols="60" placeholder="Type something here..."></textarea>
</form>
</div>
<div class="col-12 results"></div>
</div>
</div>
So, basically script should split textarea value from line that starts with "-" until next line which starts "-".
Code snippet is also available here: https://jsfiddle.net/zecaffe/f7zv3udh/1/
Why not just a split to the \n-?
$(document).ready(function() {
$("#textarea").keyup(function() {
const entered = $('#textarea').val()
const lines = entered.split(/\n-/);
let spans = "";
lines.forEach((l,i)=>{
// remove the first -
if(i===0 && l[0]==="-") l = l.slice(1)
spans += "<span style='color:red;'>- " + l + "</span><br/>";
})
$(".results").html(spans);
});
});
.row {
background: #f8f9fa;
margin-top: 20px;
padding: 10px;
}
.col {
border: solid 1px #6c757d;
}
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<div class="container">
<div class="row">
<div class="col-12">
<form>
<textarea id="textarea" rows="5" cols="60" placeholder="Type something here..."></textarea>
</form>
</div>
<div class="col-12 results"></div>
</div>
</div>

How could I add a border to make element look like a tree and keep it positioned correctly when resizing?

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>

How to clone, modify (increment some elements) before appending using jQuery?

I have an element that contains multiple elements inside it, what I need is to clone the element, but on every "new" element, I need to increment an element (the object number -see my script please-)
In the script I'm adding I need (every time I click on the button) to have : Hello#1 (by default it's the first one) but the first click make : Hello#2 (and keep on top Hello#1) second click = Hello#1 Hello#2 Hello#3 ... We need to keep the oldest hellos and show the first one.
var count = 1;
$(".button").click(function(){
count += 1;
num = parseInt($(".object span").text());
$(".object span").text(count);
var cont = $(".container"),
div = cont.find(".object").eq(0).clone();
cont.append(div);
});
.object{
width:100px;
height:20px;
background-color: gold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<button type="button" class="button">
create object
</button>
<div class="container">
<div class="object">
<p>
hello#<span>1</span>
</p>
</div>
</div>
You just have to change a little:
var count = 1;
$(".button").click(function() {
count += 1;
num = parseInt($(".object span").text());
var cont = $(".container"),
div = cont.find(".object").eq(0).clone();
div.find('span').text(count); // <------here you have to put the count
cont.append(div);
});
.object {
width: 100px;
height: 20px;
background-color: gold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<button type="button" class="button">
create object
</button>
<div class="container">
<div class="object">
<p>
hello#<span>1</span>
</p>
</div>
</div>
and if you want to simplify this more use this:
$(".button").click(function() {
var idx = ++$('.object').length; // check for length and increment it with ++
var cont = $(".container"),
div = cont.find(".object").eq(0).clone();
div.find('span').text(idx); // <------here you have to put the count
cont.append(div);
});
.object {
width: 100px;
height: 20px;
background-color: gold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<button type="button" class="button">
create object
</button>
<div class="container">
<div class="object">
<p>
hello#<span>1</span>
</p>
</div>
</div>
Use the following function, this is more modular and you can use it to update the count if you remove one of the elements
function updateCount() {
$(".object").each(function(i,v) {
$(this).find("span").text(i+1);
});
}
$(".button").click(function() {
num = parseInt($(".object span").text());
var cont = $(".container"),
div = cont.find(".object").eq(0).clone();
cont.append(div);
updateCount();
});
.object {
width: 100px;
height: 20px;
background-color: gold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<button type="button" class="button">
create object
</button>
<div class="container">
<div class="object">
<p>
hello#<span>1</span>
</p>
</div>
</div>

Categories

Resources