Wrapping multiple recurring elements with jQuery wrapAll() - javascript

Hi I have been looking into a solution for this for ages. Basically I have a repeating structure as follows:
<div class="article">
<div>
</div>
<h2>
</h2>
<div>
</div>
<div>
</div>
</div>
<div class="article">
<div>
</div>
<h2>
</h2>
<div>
</div>
<div>
</div>
</div>
I need to wrap the last three child elements of .article in a div e.g.
<div class="article">
<div>
</div>
<div class="wrapper">
<h2>
</h2>
<div>
</div>
<div>
</div>
</div>
</div>
I have tried jQuery .before() and .after() but the elements close before I can call them both. This was my last ditch effort:
(function($) {
$(document).ready( function () {
var items = $(".article > *");
for(var i = 1; i < items.length; i+=3) {
items.slice(i, i+3).wrapAll("<div class='wrapper'></div>");
};
});
})(jQuery);
This works fine for the first .article but ruins the rest of the code.
Real life example here:
http://www.blanketmedia.com.au/Cruise/hotnews.jsp

Loop over the elements, select the children, remove the first, and wrap them.
$(".article").each( function () {
$(this).children().slice(1).wrapAll('<div class="wrapper"></div>');
});
.article { background: yellow; }
.wrapper { background: red; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="article">
<div>1
</div>
<h2>2
</h2>
<div>3
</div>
<div>4
</div>
</div>
<div class="article">
<div>5
</div>
<h2>6
</h2>
<div>7
</div>
<div>8
</div>
</div>

Try this:
$('.article *:nth-child(3)').wrap("<div class='wrapper'></div>");

Here is another solution:
$('.article').each(function(){
var articleChildren = $(this).children();
var lastThreeElements = articleChildren.slice(Math.max(articleChildren.length - 3, 1));
lastThreeElements.wrapAll('<div class="wrapper">');
});
.article { background: yellow; }
.wrapper { background: red; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="article">
<div>Div 1
</div>
<h2>Header 1
</h2>
<div>Inner Div 1
</div>
<div>Inner Div 2
</div>
</div>
<div class="article">
<div>Div 1
</div>
<h2>Header 1
</h2>
<div>Inner Div 1
</div>
<div>Inner Div 2
</div>
</div>

Related

Drag and drop ranking function in Laravel form

I am making a survey form and I was to have the user drag and drop for a ranking system. I have the attached code but the tiles don't move when I try to drag the across. Is there any way to fix this or an alternative? When I try to drag an item over, it instead highlights the text.
survey.blade.php
<script>
$(function(){
item_height=$(".item").outerHeight(true);
height=(item_height+2)*($(".item").length+1);
$(".source-container,.destination-container").height(height);
$(".source .item").draggable({
revert:"invalid",
start:function(){
$(this).data("index",$(this).parent().index());
}
});
$(".destination").droppable({
drop:function(evern,ui){
if($(this).has(".item").length){
if(ui.draggable.parent().hasClass("source")){
index=ui.draggable.data("index");
ui.draggable.css({left:"0",top:"0"}).appendTo($(".source").eq(index));
}
else{
ui.draggable.css({left:"0",top:"0"}).appendTo($(this));
index=ui.draggable.data("index");
$(this).find(".item").eq(0).appendTo($(".destination").eq(index))
}
}
else{
ui.draggable.css({left:"1px",top:"1px"});
ui.draggable.appendTo($(this));
$(".destination").removeClass("ui-droppable-active");
}
}
});
$(".source").droppable({
accept: function(draggable) {
return $(this).find("*").length == 0;
},
drop:function(event,ui){
ui.draggable.css({left:"0",top:"0"}).appendTo($(this))
}
})
})
</script>
<h3>Drag and drop items on right for ranking</h3>
<div class='container'>
<div class="source-container">
<div class='source'>
<div class='item'>
<p>Item 1</p>
</div>
</div>
<div class='source'>
<div class='item'>
<p>Item 2</p>
</div>
</div>
<div class='source'>
<div class='item'>
<p>Item 3</p>
</div>
</div>
</div>
</div>
<div class='move-back'>
</div>
<div class='destination-container'>
<div class="destination" id="dest1">
<span>1</span>
</div>
<div class="destination" id="dest2">
<span>2</span>
</div>
<div class="destination" id="dest3">
<span>3</span>
</div>
</div>
For reference, I added 3 items to rank but I would like to add more.

Loop through two arrays - check if they belong to a div - append html to div

Hopefully I explain this well, here goes...
So the below HTML displays a basic version of my HTML - essentially I need to append the H4 inside client content to the hover-item when hovered over (The hover item currently displays but is blank with no text inside).
So I use document.QuerySelectorAll to generate a Node list and then convert to an Array, loop over the client array.
Inside I use two for loops to loop over the client content array and hover item array and check if they are contained within the client div...
If they are, I then want to append the h4 to the hover item. Each h4 will be unique as they will be people's names so it needs to append the correct one.
Here's the example code & image of the result on the project itself: results image
const hoverItem = document.querySelectorAll(".hover-item")
const hoverItemArray = Array.from(hoverItem);
const clients = document.querySelectorAll(".client");
const clientsArray = Array.from(clients);
const clientContent = document.querySelectorAll(".client-content");
const clientContentArray = Array.from(clientContent);
clientsArray.forEach(item => {
for (i = 0; i <= clientContentArray.length; i++) {
for (e = 0; e <= hoverItemArray.length; e++) {
if (item.contains(clientContentArray[i] && hoverItemArray[e])) {
hoverItem[e].append(clientContentArray[i].innerHTML);
}
}
}
})
<div class="results">
<div class="client">
<img class="client-image">
<div class="client-content">
<h4>Client Name</h4>
</div>
<div class="hover-item"></div>
</div>
<div class="results">
<div class="client">
<img class="client-image">
</div>
<div class="client-content">
<h4>Client Name</h4>
</div>
<div class="hover-item"></div>
</div>
<div class="results">
<div class="client">
<img class="client-image">
</div>
<div class="client-content">
<h4>Client Name</h4>
</div>
<div class="hover-item"></div>
</div>
First: You aren't listening for the "hover"-event (in JS mouseover) and need eventlisteners for that.
If you want to append the heading from the same div like the .hover-item you don't need arrays. You just need to select the previous element and its h4 then remove it and append it to the heading. Furthermore you can loop over the collection hoverItem directly without converting it to an array.
const hoverItem = document.querySelectorAll(".hover-item");
for (i = 0; i < hoverItem.length; i++) {
hoverItem[i].addEventListener('mouseover', function() {
const client_name = this.previousElementSibling.querySelector('h4');
if (client_name) {
client_name.remove();
this.append(client_name);
}
});
}
<div class="results">
<div class="client">
<img class="client-image">
<div class="client-content">
<h4>Client Name 1</h4>
</div>
<div class="hover-item">Hover me!</div>
</div>
<div class="results">
<div class="client">
<img class="client-image">
</div>
<div class="client-content">
<h4>Client Name 2</h4>
</div>
<div class="hover-item">Hover me!</div>
</div>
<div class="results">
<div class="client">
<img class="client-image">
</div>
<div class="client-content">
<h4>Client Name 3</h4>
</div>
<div class="hover-item">Hover me!</div>
</div>
If I understand you correctly you basically want to move the h4 element from the .client-content to .hover-item when both elements are within a .client element. So in you example html it would be only for the first .client element.
You can do that with the code below. I added some commentary to the code to explain and added some css with colors to visualize what is happening.
//Loop over all client divs.
document.querySelectorAll('.client').forEach((clientDiv) => {
//Check if the div contains both .client-content and .hover-item elements.
if(clientDiv.querySelector('.client-content') && clientDiv.querySelector('.hover-item')) {
//Append the h4 to to hover item.
clientDiv.querySelector('.hover-item').append(clientDiv.querySelector('.client-content h4'));
}
});
div {
display: inline-block;
}
.hover-item {
background-color: blue;
}
.client-content {
background-color: red;
}
h4 {
margin: 10px;
background-color: yellow;
}
<div class="results">
<div class="client">
<img class="client-image">
<div class="client-content">
<h4>Client Name</h4>
</div>
<div class="hover-item"></div>
</div>
<div class="results">
<div class="client">
<img class="client-image">
</div>
<div class="client-content">
<h4>Client Name</h4>
</div>
<div class="hover-item"></div>
</div>
<div class="results">
<div class="client">
<img class="client-image">
</div>
<div class="client-content">
<h4>Client Name</h4>
</div>
<div class="hover-item"></div>
</div>

Simple sort DIV by name, and number

I want to sort my div using vanilla JS by name and number. Before i sorting only list, and this is not a problem, but here i have a problem how to do this.
Maybe need create object, and save all value to him, and then sorting object?
Now i trying loop or items, but i can't get item-price or item-name in 'item'
var sortByNameBtn = document.getElementById('sortByName');
var sortByPriceBtn = document.getElementById('sortByPrice');
function sortingByName(){
var items = document.querySelectorAll('.item');
}
function sortingByPrice(){
var items = document.querySelectorAll('.item');
}
sortByNameBtn.addEventListener('click', sortingByName);
sortByPriceBtn.addEventListener('click', sortingByPrice);
.item {
background: #eee;
padding: 10px;
margin-bottom: 10px;
font-size: 20px;
display: inline-block;
border: 1px solid black;
}
<div class="items">
<div class="item">
<div class="item-price">12321</div>
<div class="item-name">Car</div>
</div>
<div class="item">
<div class="item-price">123</div>
<div class="item-name">Table</div>
</div>
<div class="item">
<div class="item-price">88</div>
<div class="item-name">Toys</div>
</div>
<div class="item">
<div class="item-price">1223</div>
<div class="item-name">Window</div>
</div>
<div class="item">
<div class="item-price">19</div>
<div class="item-name">Bad</div>
</div>
<div class="item">
<div class="item-price">50</div>
<div class="item-name">Mouse</div>
</div>
<div class="item">
<div class="item-price">500</div>
<div class="item-name">iPhone</div>
</div>
<div class="item">
<div class="item-price">100</div>
<div class="item-name">Mobile</div>
</div>
<div class="item">
<div class="item-price">12</div>
<div class="item-name">Cake</div>
</div>
<div class="item">
<div class="item-price">500</div>
<div class="item-name">Laptop</div>
</div>
</div>
<p>
<button id="sortByName">Sort by name</button>
</p>
<p>
<button id="sortByPrice">
Sort By price
</button>
</p>
Using flexbox for this could be useful cuz you can rearrange them with order, and it will save you a lot of re-rendering and moving bit's and pieces
Doe the flex layout will make it behave a little differently. So you might have to fix the css a bit.
Also the tab index might not be what you expect if you have inputs and what not.
var sortByNameBtn = document.getElementById('sortByName');
var sortByPriceBtn = document.getElementById('sortByPrice');
function sortingByName() {
var items = document.querySelectorAll('.item');
// get all items as an array and call the sort method
Array.from(items).sort(function(a, b) {
// get the text content
a = a.querySelector('.item-name').innerText.toLowerCase()
b = b.querySelector('.item-name').innerText.toLowerCase()
return (a > b) - (a < b)
}).forEach(function(n, i) {
n.style.order = i
})
}
function sortingByPrice(){
var items = document.querySelectorAll('.item')
Array.from(items).sort(function(a, b) {
// using ~~ to cast the value to a number instead of a string
a = ~~a.querySelector('.item-price').innerText
b = ~~b.querySelector('.item-price').innerText
return a - b
}).forEach(function(n, i) {
n.style.order = i
})
}
sortByNameBtn.addEventListener('click', sortingByName);
sortByPriceBtn.addEventListener('click', sortingByPrice);
.items {
display: flex;
}
.item {
background: #eee;
padding: 10px;
margin-bottom: 10px;
font-size: 20px;
display: inline-block;
border: 1px solid black;
}
<div class="items">
<div class="item">
<div class="item-price">12321</div>
<div class="item-name">Car</div>
</div>
<div class="item">
<div class="item-price">123</div>
<div class="item-name">Table</div>
</div>
<div class="item">
<div class="item-price">88</div>
<div class="item-name">Toys</div>
</div>
<div class="item">
<div class="item-price">1223</div>
<div class="item-name">Window</div>
</div>
<div class="item">
<div class="item-price">19</div>
<div class="item-name">Bad</div>
</div>
<div class="item">
<div class="item-price">50</div>
<div class="item-name">Mouse</div>
</div>
<div class="item">
<div class="item-price">500</div>
<div class="item-name">iPhone</div>
</div>
<div class="item">
<div class="item-price">100</div>
<div class="item-name">Mobile</div>
</div>
<div class="item">
<div class="item-price">12</div>
<div class="item-name">Cake</div>
</div>
<div class="item">
<div class="item-price">500</div>
<div class="item-name">Laptop</div>
</div>
</div>
<p>
<button id="sortByName">Sort by name</button>
</p>
<p>
<button id="sortByPrice">
Sort By price
</button>
</p>

Applying behavior to specific div within elements with same class

I am trying to hide div with class="input" only when hidden-span is equal to i-am-secret.
I've tried different approaches using .each(function) or .next() but could not get my head around it. In order to illustrate the example I've added the code bellow.
Please note that I can not add any id's or classes and the order of the rows may vary.
(function($) {
$('.basket__item-row').each(function() {
if ($('.hidden-span').is(":contains('i-am-secret')")) {
$(this).next().hide();
}
});
})(jQuery)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<div class="basket__item-row">
<div class="image">
<div>
I am normal div
</div>
</div>
<div class="input">
Please hide me
</div>
</div>
<div class="basket__item-row">
<div class="image">
<div>
I am extra div
<span class="hidden-span">i-am-secret</span>
</div>
</div>
<div class="input">
Please hide me
</div>
</div>
<div class="basket__item-row">
<div class="image">
<div>
I am normal div
<span class="hidden-span">another class</span>
</div>
</div>
<div class="input">
Please hide me
</div>
</div>
I would do a traversal this way...
$('.hidden-span') // Target all the hidden spans.
.filter(function () { // Filter all the span that contains the text.
return $(this).text().indexOf("i-am-secret") !== false;
})
.closest(".image") // Get the parent `.image`.
.next(".input") // Get the `.input` which is its sibling.
.hide(); // Hide it.
(function($) {
$('.hidden-span').filter(function () {
return $(this).text().indexOf("i-am-secret") !== false;
}).closest(".image").next(".input").hide();
})(jQuery)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<div class="basket__item-row">
<div class="image">
<div>
I am normal div
</div>
</div>
<div class="input">
Please hide me
</div>
</div>
<div class="basket__item-row">
<div class="image">
<div>
I am extra div
<span class="hidden-span">i-am-secret</span>
</div>
</div>
<div class="input">
Please hide me
</div>
</div>
<div class="basket__item-row">
<div class="image">
<div>
I am normal div
</div>
</div>
<div class="input">
Please hide me
</div>
</div>
Please correct the syntax errors. It should be class="basket__item-row".

Using JavaScript, how to make elements appear one by one

How to make blocks appear one by one when I click a button? One click = one block appearing.
<div class="gallery">
<div class="block">
<div class="img"></div>
</div>
<div class="block">
<div class="img"></div>
</div>
<div class="block">
<div class="img"></div>
</div>
<div class="block">
<div class="img"></div>
</div>
</div>
<button id="btn"></button>
Below is a working example using jQuery (as mentioned in your tags). A couple things to note:
I made a CSS class called hidden and added to each block.
With jQuery, I created a click handler for he button that finds the first block with the hidden class and removes that class. This has the result of displaying that block.
Here is the complete code:
<!doctype html>
<html>
<head>
<style>
.hidden { display: none; }
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
</head>
<body>
<div class="gallery">
<div class="block hidden">
<div class="img"></div>
first block
</div>
<div class="block hidden">
<div class="img"></div>
second block
</div>
<div class="block hidden">
<div class="img"></div>
third block
</div>
<div class="block hidden">
<div class="img"></div>
fourth block
</div>
</div>
<button id="btn">Click me</button>
<script>
$(function () {
$('#btn').click(function () {
$('.gallery .hidden').first().removeClass('hidden');
});
});
</script>
</body>
</html>
using only js
var count = 0;
function i(){
items = document.getElementsByClassName("block");
if(count < items.length)
items[count++].style.display = "block";
}
.block{
display: none;
}
<div class="gallery">
<div class="block">
<div class="img">1</div>
</div>
<div class="block">
<div class="img">2</div>
</div>
<div class="block">
<div class="img">3</div>
</div>
<div class="block">
<div class="img">4</div>
</div>
</div>
<button id="btn" onclick="i()">appear one by one</button>
You could just show the first one not visible. For a little improvement, instead of looking up the visibility for each function call, we cache the list of hidden blocks ($blocks) at dom load.
At the end of the function the list of visible blocks is then updated (the one that was toggled on is displayed).
Note: this does not for dynamically added blocks that are added after the initial dom load, but the code could be easily updated for that.
$blocks = $('.block:not(:visible)');
function showBlock() {
var $block = $blocks.first().css('display', 'block');
$blocks = $blocks.not( $block );
}
.block {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="gallery">
<div class="block">
<div class="img"></div>
one
</div>
<div class="block">
<div class="img"></div>
two
</div>
<div class="block">
<div class="img"></div>
three
</div>
<div class="block">
<div class="img"></div>
four
</div>
</div>
<button id="btn" onclick="showBlock()">Show Block</button>
First set all blocks invisible with css:
.block { display: none; }
Then, add a handler to the button and make the first non-visible visible:
$("#btn").on("click", function () {
$(".gallery").find(".block:hidden").first().css({ display: "block"; });
});

Categories