create dynamic elements and move others within them - javascript

basically I have several inputs in the DOM, I need to create the same amount of divs according to the inputs, once created, move each input within the created element, I'm using jquery for this, example:
<!-- first state: without inserting anything -->
<div class="container">
<input type="example-element">
<input type="example-element">
<input type="example-element">
</div>
<!-- second: inserting the dynamic elements -->
<div class="container">
<div class="dinamic-element"> </div>
<div class="dinamic-element"> </div>
<div class="dinamic-element"> </div>
<input type="example-element">
<input type="example-element">
<input type="example-element">
</div>
<!-- result: this is what I try to do -->
<div class="container">
<div class="dinamic-element">
<input type="example-element">
</div>
<div class="dinamic-element">
<input type="example-element">
</div>
<div class="dinamic-element">
<input type="example-element">
</div>
</div>

With jQuery:
// select all <input> elements within elements
// having a class of 'container':
$('.container input')
// wrapping each of those elements individually
// with the supplied HTML:
.wrap('<div class="dynamic"></div>');
$('.container input').wrap('<div class="dynamic"></div>');
.dynamic {
border: 1px solid #f90;
margin: 0 0 1em 0;
padding: 0.5em;
border-radius: 0.5em 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<input type="example-element">
<input type="example-element">
<input type="example-element">
</div>
In plain – albeit ES6 – JavaScript:
// creating a <div> element:
let div = document.createElement('div'),
// initialising an element to hold clones
// of that created <div>:
clone;
// adding the 'dynamic' class-name, via the
// HTMLElement.classList API, to the created
// <div>:
div.classList.add('dynamic');
// using Array.from() to convert the result of
// the document.querySelectorAll() method into
// an Array, in order to use Array methods:
Array.from(
// finding all <input> elements that are contained
// within element(s) with a class of 'container':
document.querySelectorAll('.container input')
// iterating over the Array of elements using
// Array.prototype.forEach(), and an anonymous
// Arrow function:
).forEach(
// input is a reference to the current <input>
// element of the Array of <input> elements over
// which we're iterating:
input => {
// here we clone the created <div> element, and
// assign that clone the 'clone' variable:
clone = div.cloneNode();
// replacing the input node with the cloned <div>:
input.replaceWith(clone);
// appending the input node to the cloned <div>:
clone.appendChild(input);
}
);
let div = document.createElement('div'),
clone;
div.classList.add('dynamic');
Array.from(document.querySelectorAll('.container input')).forEach(
input => {
clone = div.cloneNode();
input.replaceWith(clone);
clone.appendChild(input);
}
);
.dynamic {
border: 1px solid #f90;
margin: 0 0 1em 0;
padding: 0.5em;
border-radius: 0.5em 0;
}
<div class="container">
<input type="example-element">
<input type="example-element">
<input type="example-element">
</div>
And, finally, in plain – ES5 – JavaScript:
let div = document.createElement('div'),
clone;
div.classList.add('dynamic');
// here we use Function.prototype.call() to apply
// the Array.prototype.slice() to the HTMLCollection
// returned by document.querySelectorAll(), which
// is an older means of converting an Array-like
// Object into an Array:
Array.prototype.slice.call(
document.querySelectorAll('.container input')
).forEach(function(input) {
// here we use a traditional anonymous function declaration
// rather than using the aArrow function, but it behaves
// identically as above:
clone = div.cloneNode();
input.replaceWith(clone);
clone.appendChild(input);
});
let div = document.createElement('div'),
clone;
div.classList.add('dynamic');
Array.prototype.slice.call(
document.querySelectorAll('.container input')
).forEach(function(input) {
clone = div.cloneNode();
input.replaceWith(clone);
clone.appendChild(input);
});
.dynamic {
border: 1px solid #f90;
margin: 0 0 1em 0;
padding: 0.5em;
border-radius: 0.5em 0;
}
<div class="container">
<input type="example-element">
<input type="example-element">
<input type="example-element">
</div>
References:
JavaScript:
Array.from().
Array.forEach().
Array.prototype.slice().
document.querySelectorAll().
Function.prototype.call().
JavaScript Arrow functions.
ChildNode.replaceWith().
Node.appendChild().
jQuery:
wrap().

You could do this using jQuery
$( "input.example-element").wrap("<div class='dinamic-element'></div>");

Related

Why wrapping children in a container doesn't work

I am trying to wrap the children of some elements in a new div and it's not working.
When I loop through the children of the element, the loop only loops through 1 element.
It wraps only one child and leaves the other.
Why?
var homepageRows = document.querySelectorAll(".span-12 > *");
homepageRows.forEach(function(row){
var newRow = document.createElement("div");
newRow.className = "wrapper-row";
row.childNodes.forEach(function(child) {
newRow.appendChild(child);
});
row.append(newRow);
})
<div class="col sqs-col-12 span-12">
<div class="row sqs-row">
<div>Wrap me in .wrapper-row</div>
<div>Wrap me in .wrapper-row</div>
</div>
<div class="row sqs-row">
<div>Wrap me in .wrapper-row</div>
<div>Wrap me in .wrapper-row</div>
</div>
<div class="row sqs-row">
<div>Wrap me in .wrapper-row</div>
<div>Wrap me in .wrapper-row</div>
</div>
</div>
One approach is as follows, with some comments in the code to try to explain:
let homepageRows = document.querySelectorAll(".span-12 > *");
homepageRows.forEach(function(row) {
let newRow = document.createElement("div");
// here we use the Element.classList API to add a new class-name:
newRow.classList.add("wrapper-row");
// the easiest change is to simply substitute `row.children` (which retrieves
// a list of the element-children of the current `row` element-node; we use
// an Array-literal and the spread operator to convert the iterable HTMLCollection
// into an Array in order to use Array.prototype.forEach() (there is a
// NodeList.prototype.forEach(), which is available to chain to the returned
// value of document.querySelectorAll(), but there is no
// HTMLCollection.prototype.forEach() which means it's easier to use the Array version.
[...row.children].forEach(function(child) {
newRow.appendChild(child);
});
row.append(newRow);
})
*,
::before,
::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
div {
border: 2px solid currentColor;
color: black;
padding: 1em;
margin: 1em;
}
.wrapper-row,
.wrapper-row * {
color: red;
}
<div class="col sqs-col-12 span-12">
<div class="row sqs-row">
<div>Wrap me in .wrapper-row</div>
<div>Wrap me in .wrapper-row</div>
</div>
<div class="row sqs-row">
<div>Wrap me in .wrapper-row</div>
<div>Wrap me in .wrapper-row</div>
</div>
<div class="row sqs-row">
<div>Wrap me in .wrapper-row</div>
<div>Wrap me in .wrapper-row</div>
</div>
</div>
References:
document.querySelectorAll().
Element.children.
Element.classList.
Node.childNodes.
NodeList.prototype.forEach().
The return value of querySelectorAll is already an array, namely the array of all elements of that match your query. So in your case, presumably, it's just one. What you want to do, I believe, is not to iterate over that array, but over all children of the first element (the one you want):
var homepageRows = document.querySelectorAll(".span-12 > *");
homepageRows.childNodes.forEach(function(row){
...
})

jQuery .append() and .remove() skipping behavior with slider function

I'm having trouble with some dynamic HTML. I've got a slider that adds or removes DOM elements as the value changes. Each time it increases, an element is added, and each time it decreases, an element is removed.
Here's the HTML:
<input type="range" min="3" max="16" class="rgb-slider" value="3" tabindex="-1" oninput="slider(this.value)">
<div class="container">
<div class="boxes">
<div class="box"><span></span></div>
<div class="box"><span></span></div>
<div class="box"><span></span></div>
</div>
</div>
Here's the JS:
var colorCount = 3;
function slider(value) {
if (colorCount < parseInt(value)) {
$('.boxes').append('<div class="box"><span></span></div>');
colorCount = value;
} else {
$('.box:last-child').remove();
colorCount = value;
}
}
http://jsfiddle.net/meu9carx/
However, when I quickly move the slider, it seems to skip or trip up, and I end up with more or fewer than I started with. The slider has a range from 3-16, but sometimes the min value goes to more or less than 3. Sometimes, all the boxes vanish.
Is there a smarter way to code this? I'm trying to avoid hard-coding divs here.
If the mouse moves fast, it's possible for the input value to change by more than one (in either direction) during a single input event. Use the value in the input to determine how many squares there should be exactly, rather than adding or removing only a single element each time.
const boxes = $('.boxes');
$('input').on('input', function() {
const targetSquares = Number(this.value);
while (boxes.children().length < targetSquares) {
boxes.append('<div class="box"><span></span></div>');
}
while (boxes.children().length > targetSquares) {
$('.box:last-child').remove();
}
});
body{
background: #777;
font-family: 'Arimo', sans-serif;
}
.container { padding: 20px 0; }
.boxes { display: flex; }
.box {
padding: 10px;
background: #ffffff;
height: 100px;
flex: 1;
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="range" min="3" max="16" class="rgb-slider" value="3" tabindex="-1">
<div class="container">
<div class="boxes">
<div class="box"><span></span></div>
<div class="box"><span></span></div>
<div class="box"><span></span></div>
</div>
</div>

Perform operations on button values in JavaScript

I'm trying to make a calculator in JS and I'm searching for ways to add, subtract, multiply and divide button values. I've created a function to display the buttons but now I realize that that might not be necessary and I might need just one function which displays and does the operation.
HTML code:
<div class="numbers">
<button value="1" onclick="displayButtons(this)">1</button>
<button value="2" onclick="displayButtons(this)">2</button>
<button value="3" onclick="displayButtons(this)">3</button>
<button value="4" onclick="displayButtons(this)">4</button>
<button value="=" id="calculate" onclick="performOperations(this)">=</button>
**etc.**
<div class="operations">
<button value="+" onclick="displayButtons(this)" style="width: 2rem; top: 5rem;">+</button>
<button value="-" onclick="displayButtons(this)" style="left: -6rem; top: 5rem;">-</button>
**etc.**
JS code:
function displayButtons(button) {
outputDiv.innerHTML += button.value
}
function performOperations(button) {
var val = parseFloat(button.getAttribute("value"));
var total = parseFloat(document.getElementById('output').getAttribute("value"));
document.getElementById('output').innerHTML = total + val;
}
That is my attempt to do addition the button values and I have the performOperations called on the "=" sign which currently displays NaN onclick. (I'm working on the addition first).
Any push in the right direction is appreciated. Thank you!
You're right that you can use one function to do all the work but it means that you have to mark up your HTML with classes and data-attributes.
In this example I've used CSS grid to display the various calculator buttons. The "equals" and "clear" buttons have a data attribute to help the function decide what operation to do.
// Cache our elements and add an event listener
// to the button container. `handleClick` returns a
// new function that is called when the listener is fired
const output = document.querySelector('.output');
const buttons = document.querySelector('.buttons');
buttons.addEventListener('click', handleClick(), false);
function handleClick() {
// Initialise the sum
const sum = [];
// Return the function that will be called
// when a click event occurs
return function(e) {
// Because we're using event delegation (details
// below) we need to check that the element that
// was clicked was a button
if (e.target.matches('.button')) {
// Destructure the type from the dataset, and
// the text content
const { dataset: { type }, textContent } = e.target;
// `switch` on the type
switch (type) {
// If it's equals evaluate the elements in
// the array, and output it
case 'equals': {
output.textContent = eval(sum.join(''));
break;
}
// Clear empties the array, and clears
// the output
case 'clear': {
sum.length = 0;
output.textContent = '';
break;
}
// Otherwise add the textContent to
// the array, and update the output
default: {
sum.push(textContent);
output.textContent = sum.join(' ');
break;
}
}
}
}
}
.container{width:175px;}
.buttons {display: grid; grid-template-columns: repeat(4, 40px);grid-gap:0.3em;}
.button {display:flex;justify-content:center;align-items:center;background-color: #efefef; border: 1px solid #565656;padding: 0.5em;}
.button:not(.void):hover {background-color: #dfdfdf; cursor:pointer;}
.output {height: 20px; padding: 0.5em 0.2em;font-size: 1.2em;border:1px solid #565656;margin-bottom: 0.2em;}
<div class="container">
<div class="output"></div>
<div class="buttons">
<div class="button">7</div>
<div class="button">8</div>
<div class="button">9</div>
<div class="button">*</div>
<div class="button">4</div>
<div class="button">5</div>
<div class="button">6</div>
<div class="button">/</div>
<div class="button">1</div>
<div class="button">2</div>
<div class="button">3</div>
<div class="button">-</div>
<div class="button">0</div>
<div data-type="clear" class="button">C</div>
<div data-type="equals" class="button">=</div>
<div class="button">+</div>
</div>
</div>
Additional documentation
Destructuring assignment
Event delegation

Why does jQuery .text() not return the value inside the div?

I am missing something trivial here, but I don't get it. Why doesn't it alert Test Value?
$('.comment-upvote-wrapper').on('click', function() {
// Get the comments pk
var comment_pk = $(this).closest('#comment-pk').text()
alert(comment_pk)
})
.comment-upvote-wrapper {
width: max-content;
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div hidden id="comment-pk">Test Value</div>
<div class="comment-box">
<div class="comment-options">
<div class="comment-upvote-wrapper comment-options-box">Return "Text Value"
The problem is because #comment-pk is not an ancestor element of .comment-upvote-wrapper, it's a sibling of .comment-box. Therefore you need to use closest() to get the .common-box, then prev():
$('.comment-upvote-wrapper').on('click', function() {
var comment_pk = $(this).closest('.comment-box').prev('#comment-pk').text();
console.log(comment_pk)
})
.comment-upvote-wrapper {
width: max-content;
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div hidden id="comment-pk">Test Value</div>
<div class="comment-box">
<div class="comment-options">
<div class="comment-upvote-wrapper comment-options-box">Return "Text Value"</div>
</div>
</div>
However the fact that the element has an id attribute means there should only ever be one of them in the DOM. As such using DOM traversal to find it is redundant; you can just select it directly by its id:
$('.comment-upvote-wrapper').on('click', function() {
var comment_pk = $('#comment-pk').text();
console.log(comment_pk)
})

Filter elements in DOM based on data-attr with jQuery

I'm trying to filter these items with jQuery autocomplete according to their data-name, but I got stuck with it a bit. Generally, I want to start typing the text in the input field and remove items from DOM if they don't match. Any help is much appreciated.
Pen: https://codepen.io/anon/pen/aVGjay
$(function() {
var item = $(".item");
$.each(item, function(index, value) {
console.log($(value).attr("data-name"));
var everyItem = $(value).attr("data-name");
});
$("#my-input").autocomplete({
source: everyItem, //?
minLength: 1,
search: function(oEvent, oUi) {
// get current input value
var sValue = $(oEvent.target).val();
// init new search array
var aSearch = [];
// for each element in the main array
$(everyItem).each(function(iIndex, sElement) {
// if element starts with input value
if (sElement.substr(0, sValue.length) === sValue) {
// add element
aSearch.push(sElement);
}
});
// change search array
$(this).autocomplete("option", "source", aSearch);
}
});
});
.items {
width: 200px;
}
.item {
background-color: red;
margin-top: 2px;
}
<input type="text" placeholder="Filter items" id="my-input">
<div class="items">
<div class="item" data-name="one">one</div>
<div class="item" data-name="two">two</div>
<div class="item" data-name="three">three</div>
<div class="item" data-name="four">four</div>
</div>
It's a little odd to use autocomplete for this, as that's intended to build a filtered option list from a provided object or remote data source, not from DOM content.
You can build the functionality yourself by attaching an input event listener to the #my-input which in turn goes through the .item elements and uses a regular expression to filter ones with matching data-name attributes and displays them, something like this:
$(function() {
var $items = $(".item");
$('#my-input').on('input', function() {
var val = this.value;
$items.hide().filter(function() {
return new RegExp('^' + val, 'gi').test($(this).data('name'));
}).show();
});
});
.items {
width: 200px;
}
.item {
background-color: red;
margin-top: 2px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" placeholder="Filter items" id="my-input">
<div class="items">
<div class="item" data-name="one">one</div>
<div class="item" data-name="two">two</div>
<div class="item" data-name="three">three</div>
<div class="item" data-name="four">four</div>
</div>

Categories