Remove repeated after 1st occurrence - javascript

I'm trying to clean up the results presented on my HTML file with Jquery. I want to keep removing words that are repeated more than one time.
A quick example
Accents Australian
Accents English (RP)
Dance Hip Hop
Dance Jazz
It should be output as
Accents
Australian
English (RP)
Dance
Hip Hop
Jazz
My original HTML looks like this
<div role="list" class="skill-items">
<div role="listitem" class="skill-item">
<div class="skill-category">Accents</div>
<div>Australian</div>
</div>
<div role="listitem" class="skill-item">
<div class="skill-category">Accents</div>
<div>English (RP)</div>
</div>
<div role="listitem" class="skill-item">
<div class="skill-category">Dance</div>
<div>Hip Hop</div>
</div>
<div role="listitem" class="skill-item">
<div class="skill-category">Dance</div>
<div>Jaz</div>
</div>
</div>
I tried my best but I'm not landing in a good place
$('.skill-category').text(function(index, oldText) {
return oldText.replace($(this).parent().next().find('.skill-category').text(), '');
})
Any suggestion?

Please check below working code:
const category = [...document.querySelectorAll('.skill-item > .skill-category')];
const texts = new Set(category.map(x => x.innerHTML));
category.forEach(category => {
if(texts.has(category.innerHTML)){
texts.delete(category.innerHTML);
}
else{
category.remove()
}
})
As per you question and shared HTML above is the working code for the same and if you add more similar things it will help.
Please let me know if you find any issues

Your question can be broken into two problems:
You want to group the elements with the same value for .skill-category
You want to change <div> elements into a list.
Grouping the elements could by done like so:
For every category, take a look at the previous element.
Does it contain the same category? If not, then continue to the next category.
If so, take everything after .skill-category (in your example HTML, that's a single <div>. Cut-and-paste it at the end of the aforementioned previous element.
For the second problem:
Changing an element (<div> to <li>) is not possible. You can create a new <li> and move what's inside the <div> into it. Of course, you'll need a <ul> that wraps the <li>s as well.
Take the .skill-category elements
Find all the content that follows the category (in your case, 1+ <div> elements)
Put the contents of the matched elements into a new <li>.
Put all the <li>s of a single category into a <ul>.
Remove the matched elements (in your case, the <div>(s)) since we've moved all their content to a different node. They're now empty tags and useless.
Put the <ul> after the .skill-category.
// Grouping the results.
$('.skill-category').each(function() {
// Get the previous .skill-item and find the category.
var prev = $(this).parent().prev('.skill-item').find('.skill-category');
// Check if the previous category === this category.
var same = !!(prev.length && prev.text() === $(this).text());
if (!same) {
return; // Do nothing.
}
// Take every element after the category and move it to the
// previous .skill-item.
prev.after($(this).nextAll());
// Then remove the now-empty category.
// All content has been moved to the previous element, after all.
$(this).parent().remove();
});
// Wrapping the contents of a category in a list.
$('.skill-category').each(function() {
var list = $('<ul></ul');
// Find everything after the category.
$(this).nextAll().each(function() {
// Create a <li> and move the child elements to it.
// Then add the <li> to the <ul>.
$('<li></li>').append($(this).contents()).appendTo(list);
}).remove(); // remove the now empty elements.
// Add the list to current .skill-category.
$(this).append(list);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div role="list" class="skill-items">
<div role="listitem" class="skill-item">
<div class="skill-category">Accents</div>
<div>Australian</div>
</div>
<div role="listitem" class="skill-item">
<div class="skill-category">Accents</div>
<div>English (RP)</div>
</div>
<div role="listitem" class="skill-item">
<div class="skill-category">Dance</div>
<div>Hip Hop</div>
</div>
<div role="listitem" class="skill-item">
<div class="skill-category">Dance</div>
<div>Jaz</div>
</div>
</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>

Getting text content of a div element excluding its children

I have the following HTML code and I need to console.log only Shipping.
I tried a few methods but can't seem to get it to work.
I tried selecting first its children and printing out the textContent of its parent - no go. I could delete its children and print out what's left but I can't do that.
Any suggestions?
<div class="accordion shadowed-box shipping collapsed summary">
<fieldset>
<legend>
Shipping
<div id="shippingTooltip" class="form-field-tooltip cvnship-tip" role="tooltip">
<span class="tooltip">
<div class="tooltip-content" data-layout="small tooltip-cvn">
<div id="cart-checkout-shipping-tooltip" class="html-slot-container">
<p>We ship UPS, FedEx and/or USPS Priority Mail.<br>
<a class="dialogify" data-dlg-options="{"height":200}" href="https://www.payless.com/customer-service/ordering-and-shipping/cs-ordering-shipping-schedule.html" title="shipping information">Learn more about our shipping methods and prices.</a>
</p>
</div>
</div>
</span>
</div>
Edit
</legend>
</fieldset>
</div>
I tried this:
var accordionChildren = document.querySelector('.accordion.shadowed-box.shipping>fieldset>legend *');//selects the first child
var accordionTitle = accordionChildren.parentElement;
var text = accordionTitle.textContent;
console.log(text);
I want to get Shipping but instead I get still all the text contents of the legend element.
you can access Text nodes by iterating over the child nodes (or access the intended node directly) of the accordionTitle variable.
let textNode = accordionTitle.childNodes[0],
text = textNode.textContent;
console.log(text);
See https://developer.mozilla.org/en-US/docs/Web/API/Node/childNodes and https://developer.mozilla.org/en-US/docs/Web/API/Text
You just need to find the TextNode child from all of the elements children, you do this by iterating over all of the childNodes and when the node type matches TextNode, return its textContext.
For a jQuery based solution on how to pick the TextNode child of an element see this question - but my example shows how to do it in vanilla ES (with a for loop over childNodes):
Object.defineProperty(HTMLElement.prototype, 'onlyTextContent', {
enumerable: false,
configurable: false,
get: function() {
for(let i = 0; i < this.childNodes.length; i++) {
if(this.childNodes[i].nodeType === Node.TEXT_NODE) {
return this.childNodes[i].textContent.trim();
}
}
return null;
}
});
document.addEventListener('DOMContentLoaded', function() {
console.log(
document.getElementById('legend1').onlyTextContent
);
});
<div class="accordion shadowed-box shipping collapsed summary">
<fieldset>
<legend id="legend1">
Shipping
<div id="shippingTooltip" class="form-field-tooltip cvnship-tip" role="tooltip">
<span class="tooltip">
<div class="tooltip-content" data-layout="small tooltip-cvn">
<div id="cart-checkout-shipping-tooltip" class="html-slot-container">
<p>We ship UPS, FedEx and/or USPS Priority Mail.<br>
<a class="dialogify" data-dlg-options="{"height":200}" href="https://www.payless.com/customer-service/ordering-and-shipping/cs-ordering-shipping-schedule.html" title="shipping information">Learn more about our shipping methods and prices.</a>
</p>
</div>
</div>
</span>
</div>
Edit
</legend>
</fieldset>
</div>
You can get the contents of the <legend> tag as a string and then use a regular expression to remove the HTML tags and their content inside. Like this:
let legends = document.querySelector('.accordion.shadowed-box.shipping>fieldset>legend');
let title = legends.innerHTML.replace(/<.*/s, '');
// title = "Shipping"
The regular expression matches the first < character and everything that follows. So we replace that match with an empty string ''.

jQuery Grouping by first letter

I have list of posts in Wordpress its looks like this:
<div class="products uk-grid uk-grid-width-medium-1-4">
<h3>AShape(14)</h3>
<h3>AShape(20)</h3>
<h3>CShape(38)</h3>
<h3>FShape(1)</h3>
<h3>FShape(4)</h3>
<h3>ZShape(2)</h3>
<h3>ZShape(24)</h3>
</div>
I need to find some way to pass all links through script and transform it in letter groups. So it should take first letter from all <h3> of links and make groups like this:
<div class="products uk-grid uk-grid-width-medium-1-4">
<div>
<span>A</span>
<h3>AShape(14)</h3>
<h3>AShape(20)</h3>
</div>
<div>
<span>C</span>
<h3>CShape(38)</h3>
</div>
<div>
<span>F</span>
<h3>FShape(1)</h3>
<h3>FShape(4)</h3>
</div>
<div>
<span>Z</span>
<h3>ZShape(2)</h3>
<h3>ZShape(24)</h3>
</div>
</div>
How i can do it using jQuery?
here i have simple codepen: http://codepen.io/ponciusz/pen/EPgQKP
You could iterate over each of the children elements and create corresponding containers for each letter. In the example below, a div container is appending with a custom data-letter attribute if a container does not already exist for that letter.
As I mentioned in the comments, I'd also suggest placing the a element inside of the h3 elements as well:
$('.products > h3').each(function () {
var letter = $('a', this).text().charAt(0);
if (!$(this).parent().find('[data-letter="'+ letter +'"]').length) {
$(this).parent().append('<div data-letter="'+ letter+'"><span>'+ letter +'</span></div>');
}
$(this).parent().find('[data-letter="'+ letter +'"]').append(this);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="products uk-grid uk-grid-width-medium-1-4">
<h3>AShape(14)</h3>
<h3>AShape(20)</h3>
<h3>CShape(38)</h3>
<h3>FShape(1)</h3>
<h3>FShape(4)</h3>
<h3>ZShape(2)</h3>
<h3>ZShape(24)</h3>
</div>

Find index of item within a specific div where multiple divs exist

I have a HTML set up of many images, wrapped in divs, which are wrapped 4 items to a row. Example, 4 items per row, 2 rows:
<div class="staff-wrapper">
<div class="staff-row">
<div class="staff-item">
<img>
</div>
<div class="staff-item">
<img>
</div>
<div class="staff-item">
<img>
</div>
<div class="staff-item">
<img>
</div>
</div>
<div class="staff-row">
<div class="staff-item">
<img>
</div>
<div class="staff-item">
<img>
</div>
<div class="staff-item">
<img>
</div>
<div class="staff-item">
<img>
</div>
</div>
</div>
I want to be able to identify, when clicking on the image, which index this item has within its row.
So if the 3rd item of a row is clicked, the value will be 3. The possible answers should only ever be 1,2,3 or 4 because there can only ever be maximum 4 items per row.
I almost have this, however I'm finding the result is always based on the whole of the list of items, so I'm getting answers like 7 for the last item. Instead this should be 3`, because it's the third item in it's row.
Here is what I have:
var itemPosition = $(".staff-row img").index(this);
Any ideas?
JS Fiddle https://jsfiddle.net/vo6hz1zc/
You should be finding the index of the parent of the this which is img that's clicked.
Also add 1 as indexes start from 0
$('.staff-item img').click(function(){
var itemPosition = $(this).parent().index() + 1;
console.log(itemPosition);
});
JSFiddle
What you want is getting the right row first, and then get the index you could loop trough the rows:
$('.staff-item img').click(function(){
$this=$(this);
var itemPosition = $(".staff-row").each(function(){
itemPosition=$("img",$(this)).index($this);
if(itemPosition>-1){
console.log(itemPosition);
}
});
});
A better option is just select the child from this if thats possible is selecting the parent and search in the parent:
$('.staff-item img').click(function(){
var itemPosition = $(this).parent().index();
console.log(itemPosition)
});
Try this.
$('.staff-item img').click(function(){
var itemPosition = $(this).closest(".staff-row").find(".staff-item img").index(this);
alert(itemPosition);
});
Updated your jsfiddle https://jsfiddle.net/prakashlaxkar/vo6hz1zc/3/ as well. Thanks

How to show the first n number of elements in jQuery?

I have a page that has 50 elements with the same class "fields" which are all display none at the moment
<div class="fields" style="display:none;">
...
</div>
<div class="fields" style="display:none;">
...
</div>
<div class="fields" style="display:none;">
...
</div>
<div class="fields" style="display:none;">
...
</div>
...
How to I only show the first 3 or whatever number. Plus count them with a count on top like the following example below.
So for example if I needed the first 3 this is what i need the divs to look like
<div class="fields">
<h1>Station 1</h1>
</div>
<div class="fields">
<h1>Station 2</h1>
</div>
<div class="fields">
<h1>Station 3</h1>
</div>
<div class="fields" style="display:none;">
...
</div>
...
So basically only some the number of divs that I need...I already have the number of elements I need to show in this blur statement in the station_count variable. Also notice i need a span tag with the count..any ideas on how to do this
$("#number_station").blur(function(){
var station_count = $(this).val();
//code goes there
});
How to I only show the first 3 or whatever number.
$('div.fields:lt(3)').show();
Plus count them with a count on top
$('div.fields:lt(3)').each(function (index)
{
$('<h1></h1>', {text: 'Station ' + index}).prependTo(this);
}).show();
Demo: http://jsfiddle.net/mattball/TssUB/
Read the jQuery API docs for basic questions like this:
:lt() selector
.prependTo()
jQuery() (for creating new elements)
While the other answers will work, I recently discovered and love the jQuery slice() method.
$(".fields").slice(0, 3).each(function(index) {
// Do whatever you want to the first three elements
}
With
$(".fields").each(function() {
//do whatever like count then show/hide
});
you can iterate over the hidden divs. So with a simple variable you can start/stop whenever you need.

Categories