Container structure issue with JQuery - Containing containers - javascript

I'm finishing up a memory game for school and I'd really like the cards to flip with a CSS animation, which on it's own is pretty straight forward. However I'm pretty new to JavaScript and JQuery which is leading to some trouble with achieving the proper container structure I need to make the cards flip when they are clicked.
Presently the game pieces generate within the board as follows:
const generate=(cards)=>{
cards.forEach(function(card, i) {
$(".gameBoard")
.append($("<div>").addClass("front")//
.append($("<div>").addClass("back").append($("
<img>").attr("src", cards[i]))));
});
};
OR:
<div class="gameBoard>
<div class="front"></div>
<div class="back"><img src="cards"></div>
</div>
But in order for the animation to function properly both the front and back divs need to exist in the same container like this:
<div class="gameBoard>
<div class="flip">
<div class="front></div>
<div class="back"><img src="cards></div>
</div>
</div>
How can I add the div I need (.flip) but have it contain the front and back divs, not just append on to the other divs being generated within the .gameboard container.
Thanks.

It's much simpler to create your DOM using template literals rather than jQuery methods. That way you just describe the HTML as you're accustomed to.
const generate=(cards)=>{
cards.forEach(function(card, i) {
$(".gameBoard").append(`
<div class=flip>
<div class=front></div>
<div class=back><img src="${cards[i]}"</div>
</div>
`);
});
};
generate([
"https://dummyimage.com/180x120/f00/fff.png&text=one",
"https://dummyimage.com/180x120/0f0/fff.png&text=two",
"https://dummyimage.com/180x120/00f/fff.png&text=three",
]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<div class=gameBoard></div>
You'll notice the ${cards[i]}, which lets you perform string interpolation by executing at runtime the code in the braces.
Here's a vanilla JS version.
const generate=(cards)=>{
var gb = document.querySelector(".gameBoard");
cards.forEach(card =>
gb.insertAdjacentHTML("beforeend", `
<div class=flip>
<div class=front></div>
<div class=back><img src="${card}"</div>
</div>
`)
);
};
generate([
"https://dummyimage.com/180x120/f00/fff.png&text=one",
"https://dummyimage.com/180x120/0f0/fff.png&text=two",
"https://dummyimage.com/180x120/00f/fff.png&text=three",
]);
<div class=gameBoard></div>
It also uses card instead of cards[i], and an arrow function for the callback.
And this one performs a single append.
const generate=(cards)=>{
document.querySelector(".gameBoard")
.insertAdjacentHTML("beforeend", cards.map(card =>
` <div class=flip>
<div class=front></div>
<div class=back><img src="${card}"</div>
</div>`).join(""));
};
generate([
"https://dummyimage.com/180x120/f00/fff.png&text=one",
"https://dummyimage.com/180x120/0f0/fff.png&text=two",
"https://dummyimage.com/180x120/00f/fff.png&text=three",
]);
<div class=gameBoard></div>

Related

How can I get the path to a specific DOM element iterating a HTML collection?

This is the issue: I want to have nested divs with paragraphs inside with different texts.
I want to be able to get the paragraph that contains certain word, for example "mate" I did the below HTML structure trying to obtain an HTML collection and iterate it, and then using javascript, try to use the includes method to get the paragraph than contains that word, and finally, try to find a way to get the full path from the uppermost div to this p.
<div class="grandpa">
<div class="parent1">
<div class="son1">
<p>I like oranges</p>
</div>
<div class="son2">
<p>yeeeey</p>
<p>wohoo it's saturday</p>
</div>
<div class="son3"></div>
</div>
<div class="parent2"></div>
<div class="parent3">
<div class="son1">
<p>your team mate has been killed!</p>
<p>I should stop playing COD</p>
</div>
<div class="son2"></div>
</div>
</div>
I actually don't know how to achieve it, but at least I wanted to get an HTML collection to iterate, but I'm not being able to get it.... When I use this:
const nodes = document.querySelector('.grandpa');
console.log(typeof nodes);
I don't get an HTML collection, instead if I console.log typeof nodes variable it says it is an object..
How can I iterate this DOM tree, capture the element that contais the word "mate", and obtain (this is what I really want to achieve) the path to it?
Thanks!
You can loop through every element, remove all children elements, then check whether the textContent includes the string you are looking for:
const allElements = document.body.querySelectorAll('*');
const lookFor = "mate";
var elem;
for (let i = 0; i < allElements.length; i++) {
const cur = allElements[i].cloneNode(true); //doesn't mess up the original element when removing children
while (cur.lastElementChild) {
cur.removeChild(cur.lastElementChild);
}
if (cur.textContent.includes(lookFor)) {
elem = cur;
break;
}
}
console.log(elem);
<div class="grandpa">
<div class="parent1">
<div class="son1">
<p>I like oranges</p>
</div>
<div class="son2">
<p>yeeeey</p>
<p>wohoo it's saturday</p>
</div>
<div class="son3"></div>
</div>
<div class="parent2"></div>
<div class="parent3">
<div class="son1">
<p>your team mate has been killed!</p>
<p>I should stop playing COD</p>
</div>
<div class="son2"></div>
</div>
</div>

How to get the first class from two same classes with cheerio

currently i'm making a web scraper with node.Js using cheerio. i want to get the data inside the first of class="panel-items-list.
the html look like this
<div class="margin-bottom-=30">
<div class="side-list-panel">
<ul class="panel-item-list">...</ul>
</div>
</div>
<div class="margin-bottom-=30">
<div class="side-list-panel">
<ul class="panel-item-list">...</ul>
</div>
</div>
i already did like this
const relateArticle = $('.panel-items-list').map((i, section)=>{
let articles = $(section).find('h5');
return articles.text();
}).get();
but it return the data from both class="panel-items-list". how do i get data just from one class ? sorry my english is bad. thanks in advance !
To get the first class only from the .panel-item-list use .get(0) that means you are only selecting the first index found using .map
Also, in your current code jQuery your are not selecting the right class selector.
In addition use .trim() method when getting the text to clean up any unseen spaces within the text.
Live Working Demo:
const relateArticle = $('.panel-item-list').map((i, section) => {
let articles = $(section).find('h5');
return articles.text().trim();
}).get(0)
console.log(relateArticle) //Foo
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="margin-bottom-=30">
<div class="side-list-panel">
<ul class="panel-item-list">
<h5>
Foo
</h5>
</ul>
</div>
</div>
<div class="margin-bottom-=30">
<div class="side-list-panel">
<ul class="panel-item-list">
<h5>
Bar
</h5>
</ul>
</div>
</div>
Use first():
$('.panel-items-list').first().find('h5').text()

addClass to multiple distinct elements with jquery

I have some code that generates markup similar to this:
<div id="container">
<div data-number="123">Fred</div>
<div data-number="128">Wilma</div>
<div data-number="129">Barney</div>
<div data-number="123">Fred</div>
<div data-number="123">Fred</div>
<div data-number="129">Barney</div>
<div data-number="111">Dino</div>
<div data-number="008">Betty</div>
<div data-number="123">Fred</div>
<div data-number="123">Fred</div>
</div>
The container will have many duplicates.
The data tags are generated dynamically based on potentially thousands of data-ids. But, a particular view will likely not have more than a dozen or so unique items, so iterating over them shouldn't be a big deal.
What I want to do is add css classes to similar items in a predictable way.
<div class="unique-item-1" data-number="123">Fred</div>
<div class="unique-item-2" data-number="008">Betty</div>
<div class="unique-item-3" data-number="128">Wilma</div>
<div class="unique-item-4" data-number="129">Barney</div>
<div class="unique-item-5" data-number="111">Dino</div>
<div class="unique-item-1" data-number="123">Fred</div>
<div class="unique-item-1" data-number="123">Fred</div>
You'll notice that all the Fred divs (data-number 123) get the same class (unique-item-1) added.
The goal is to have CSS that will add colors or whatever, like so:
.unique-item-1 {color:red;}
.unique-item-2 {color:pink;}
.unique-item-3 {color:green;}
.unique-item-4 {color:black;}
.unique-item-5 {color:wheat;}
I've tried using jquery's "unique"... but I guess I don't know what unique means :)
Use data() attribute in jquery ,
var getArray = [];
$("#container").children("[data-number]").filter(function () {
var index = $.inArray($(this).data("number"), getArray);
if (index == -1) {
getArray.push($(this).data("number"));
$(this).addClass("unique-item-" + getArray.length);
} else {
$(this).addClass("unique-item-" + parseInt(index+1));
}
});
UPDATED DEMO

Jquery Text Swap - Multiple Use Issue

So I used a script that I found on Stack Overlow to swap text. It worked great initially but then I tried to use it again on the same page and I noticed an issue.
You can see the problem here: JsFiddle
The HTML
<div class="gallerycard">
<div id="textMessage"></div>
<div class="textContent">
<div class="girlname">ONE LEFT</div>
</div>
<div class="textContent">
<div class="newgirl">TWO LEFT</div>
</div>
<div class="girlimage"></div>
<div class="girlinfo">TEXT</div>
</div>
<div class="gallerycard">
<div id="textMessage"></div>
<div class="textContent">
<div class="girlname">ONE RIGHT</div>
</div>
<div class="textContent">
<div class="newgirl">TWO RIGHT</div>
</div>
<div class="girlimage"></div>
<div class="girlinfo">TEXT</div>
</div>
The Jquery
var cnt=0, texts=[];
// save the texts in an array for re-use
$(".textContent").each(function() {
texts[cnt++]=$(this).text();
});
function slide() {
if (cnt>=texts.length) cnt=0;
$('#textMessage').html(texts[cnt++]);
$('#textMessage')
.fadeIn('fast').animate({opacity: 1.0}, 800).fadeOut('fast',
function() {
return slide()
}
);
}
slide()
So, how do I keep them from merging?
You need two arrays, one for each,
give each one of the gallerycards different ids
and do it twice
var cnt=0, firstTexts=[], secondTexts=[];
// save the texts in an array for re-use
$('#firstID > .textContent').each(function() {
firstTexts[cnt++]=$(this).text();
});
cnt = 0;
// save the texts in an array for re-use
$('#secondID > .textContent').each(function() {
secondTexts[cnt++]=$(this).text();
});
and call slide twice with the relevant array and id
There are multiple problems based entirely on too much copy/paste without understanding the why.
Both target divs have the same id. You should never have two elements on the same page which share the same id. Now there is a quick and dirty way to clean this up and there is a flexible and effective way to clean this up. I went for the flexible solution and I'll explain how it works as best I can.
<div class="gallerycard" data-target="textMessageLeft">
<div id="textMessageLeft"></div>
<div class="textContent">
<div class="girlname">ONE LEFT</div>
</div>
<div class="textContent">
<div class="newgirl">TWO LEFT</div>
</div>
<div class="girlimage"></div>
<div class="girlinfo">TEXT</div>
</div>
<div class="gallerycard" data-target="textMessageRight">
<div id="textMessageRight"></div>
<div class="textContent">
<div class="girlname">ONE RIGHT</div>
</div>
<div class="textContent">
<div class="newgirl">TWO RIGHT</div>
</div>
<div class="girlimage"></div>
<div class="girlinfo">TEXT</div>
</div>
Notice I added a data-target element to the gallerycard containing the id of the div we want to place the text into. I also changed the ids on each target div to be unique. This is critical to make it all work, as is the data-target element matching those ids.
texts = {};
// save the texts in an array for re-use
$(".textContent").each(function () {
var target = $(this).parent().attr('data-target');
if (texts[target] == null) { texts[target] = []; }
texts[target].push($(this).text());
});
function slide(divId, cnt) {
if (cnt >= texts[divId].length) cnt = 0;
$('#' + divId).html(texts[divId][cnt++]);
$('#' + divId)
.fadeIn('fast').animate({
opacity: 1.0
}, 800).fadeOut('fast',
function () {
return slide(divId,cnt)
});
}
for (var t in texts)
{
slide(t, 0);
}
In the javascript I changed a lot to make this an expandable and flexible solution, rather than simply duplicating what was already there with two separate names.
First, we removed the counter and changed texts to an object ({} instead of []). From here I can use texts like a hash, which simplifies the rest of the script. The key of the hash is the value of the data-target from the container div of the message and content divs. Add as many content divs as you want under each parent and they'll all be found and associated correctly.
The texts from each textContent div are stored in an array, but we are using the push() function to eliminate the need for a counter variable - counters are fine for a single instance, but they get ugly with multiples.
I changed the slide function to accept two variables: divId and cnt. divId is how the slider knows which div to target and cnt allows the recursive call to keep a private counter which will not conflict with other instances of the slider function running simultaneously.
Finally, to again prevent duplication and allow further expansion, Instead of simply calling slide, we iterate through the hash to get the divId and call a slide instance for each divId we have. Go ahead and try expanding the number of panes or adding new textContent divs under one of the headers. It all works very smoothly now.
The fiddle is here: http://jsfiddle.net/AX4LC/4/

How to order divs with JQUERY

i have divs like this:
<div class="oferta SYB">
<div class="infoferta">
<div class="datosoferta">
<div class="ofertapagas">Pagás<br/> $ 67</div>
<div class="ofertavalor">Valor<br /> $ 160</div>
<div class="ofertadescuento">Descuento $ 93</div>
</div>
</div>
i want to order the divs with the class="oferta" by the value in "ofertapagas" or the value in "ofertavalor" or the value in "ofertadescuento", i dont know how to, i cannot use the database, i just can use Jquery, i am using the last version of it.
Some Help please!!
jQuery abstracts Array.prototype.sort. Since jQuery wrappet sets are Array like objects, you can just call .sort() on them or apply sort on them.
var $datosoferta = $('.datosoferta');
$datosoferta.children().detach().sort(function(a,b) {
return +a.textContent.split(/\$/)[1].trim() - +b.textContent.split(/\$/)[1].trim();
}).appendTo($datosoferta);
See http://typeofnan.blogspot.com/2011/02/did-you-know.html
Demo: http://jsfiddle.net/Rfsfs/
Using ui. JQuery UI - Sortable Demos & Documentation
For your code;
$( ".offer" ).sortable({
update: function(event, ui) { // do post server. }
});
If you place those offer DIVs in some sort of container (another DIV?) you can then fetch all the offers and sort by their childrens' values.
In the HTML below, I put the offer divs inside a container div, and added a data-value attribute to each of the child divs containing data, for easier access (no regular expressions required).
<div id="ofertas_container">
<div class="infoferta">
<div class="datosoferta">
<div class="ofertapagas" data-value="67">Pagá $ 67</div>
<div class="ofertavalor" data-value="130">Valor $ 130</div>
<div class="ofertadescuento" data-value="93">Descuento $ 93</div>
</div>
</div>
<div class="infoferta">
<div class="datosoferta">
<div class="ofertapagas" data-value="57">Pagá $ 57</div>
<div class="ofertavalor" data-value="150">Valor $ 150</div>
<div class="ofertadescuento" data-value="43">Descuento $ 43</div>
</div>
</div>
<div class="infoferta">
<div class="datosoferta">
<div class="ofertapagas" data-value="107">Pagá $ 107</div>
<div class="ofertavalor" data-value="250">Valor $ 250</div>
<div class="ofertadescuento" data-value="1000">Descuento $ 1000</div>
</div>
</div>
</div>
Pagá
Valor
Descuento
The JS / jQuery function:
ofertas_sort = function(sort_key) {
// array of offer divs
var ofertas = $('.infoferta');
// the div classname corresponding to the key by which
// we are sorting
var sort_key_sel = 'div.' + sort_key;
// in large lists it'd be more efficient to calculate
// this data pre-sort, but for this instance it's fine
ofertas.sort(function(a, b) {
return parseInt($(sort_key_sel, a).attr('data-value')) -
parseInt($(sort_key_sel, b).attr('data-value'));
});
// re-fill the container with the newly-sorted divs
$('#ofertas_container').empty().append(ofertas);
};
Here's the code on jsFiddle, with some links at the bottom of the HTML to demo the sorting: http://jsfiddle.net/hans/Rfsfs/2/
I changed some of the values to make the sorting more visible.

Categories