I have a list which looks kind of like this:
<ul class="mylist">
<li>AAA</li>
<li>AAA</li>
<li>AAA</li>
<li>BBB</li>
<li>BBB</li>
<li>CCC</li>
</ul>
I would now like to add the first letter of the content above the content group.
Here is an example of how the result would look:
<ul class="mylist">
<li><strong>A</strong></li>
<li>AAA</li>
<li>AAA</li>
<li>AAA</li>
<li><strong>B</strong></li>
<li>BBB</li>
<li>BBB</li>
<li><strong>C</strong></li>
<li>CCC</li>
</ul>
How can I do this?
Fairly simple with jQuery - see the code comments for explanation.
// shorthand for on document load
$(function() {
// a variable to store our current first letter
var currentFirstLetter;
// for every child of mylist
$('.mylist').children().each(function() {
// take the first character of its content
var thisLetter = $(this)[0].innerHTML.substr(0,1).toLowerCase();
// if its different to our current first letter then add the required element
// and update our current first letter
if (thisLetter !== currentFirstLetter) {
$(this).before("<li><strong>" + thisLetter.toUpperCase() + "</strong></li>");
currentFirstLetter = thisLetter;
}
});
});
jsFiddle
Related
Web Mind Help! I have html with many lists looks like
<li>A.</li>
<li>B.</li>
<li><a class=tr href="#">C.</a></li>
<li class=tr>D.</li>
<li class=notr>E.</li>
I want select all untranslated innerText
document.querySelectorAll("li:not(.notr):not(.tr)")
Problem is if class TR not in LI i cannot filter it
li:not(.notr):not(.tr)+li>a:not(.tr) - returns empty NodeList
It seems to be a simple question but I'm confused
Like I said in the comments, the solution is to fetch this collection first, then refine the results by running another filter over that.
let rawresult = document.querySelectorAll("li:not(.notr):not(.tr)");
console.log('raw results:');
rawresult.forEach(el => console.log(el.innerText));
let refinedresult = [];
rawresult.forEach(function(el) {if (el.querySelector(".notr,.tr") == null) refinedresult.push(el);});
console.log('refined results:');
refinedresult.forEach(el => console.log(el.innerText));
<ul>
<li>A.</li>
<li>B.</li>
<li><a class=tr href="#">C.</a></li>
<li class=tr>D.</li>
<li class=notr>E.</li>
</ul>
I'm having a problem with a simple tabbed page.
It all works OK if I hard code the 'onclicks' in the tabs that appear at the top of the page like this:
<ul class="tabs" data-persist="true">
<li class='tablinks' onclick="openTab('view1')" id='default'>How to find Maolbhuidhe</li>
<li class='tablinks' onclick="openTab('view2')">How to get to Mull</li>
<li class='tablinks' onclick="openTab('view3')">Map of Mull</li>
<li class='tablinks' onclick="openTab('view4')">Map of Scotland</li>
</ul>
There's a JS file containing the function 'openTab', of course.
But when I try to add the onclick events via JS/jQuery after the page has loaded, I'm running into a problem. The HTML for this section now looks like this:
<ul id='toptabs' class="tabs" data-persist="true">
<li class='tablinks' id='default'>How to find Maolbhuidhe</li>
<li class='tablinks'>How to get to Mull</li>
<li class='tablinks'>Map of Mull</li>
<li class='tablinks'>Map of Scotland</li>
</ul>
The JS script I'm using to add the onclick events is:
function applyClicks() {
var toptabs = document.getElementById("toptabs");
var lnks = toptabs.getElementsByTagName("li");
for (var i=0; i<lnks.length; i++) {
var k = (i+1)
var vw = 'view' + k;
alert ('vw is: ' + vw);
lnks[i].onclick = (function() {
openTab('view' + k);
});
}
}
The problem seems to lie in providing the parameter to 'openTab()'. I've tried several variations, the one shown ends up as "openTab('view' + k)" (As seen in Inspector DOM). If I hard code it as 'view1' it works, but of course all the links are then the same, so only the first tab can be shown. It seems whatever I put in the JS function as the parameter gets treated as a literal.
What do I need to do to make the parameter 'view1', 'view2', 'view3', 'view4' (as in the hard coded version) according to the value of i ? This was the purpose of the var 'vw', which duly shows all the right values in turn as the script runs, but just shows up in the link on the page as 'vw'
I've also tried the widely recommended 'addEventListener('click', ...) etc. but I get the same problem (or something similar). It may be better to add an event listener eventually, but first I need to resolve the problem of passing the variable to the 'Click'.
Try it like this:
$('.tablinks').click(function(){
let idx = $(this).index() + 1;
//alert('view' + idx);
openTab('view' + idx);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<ul id='toptabs' class="tabs" data-persist="true">
<li class='tablinks' id='default'>How to find Maolbhuidhe</li>
<li class='tablinks'>How to get to Mull</li>
<li class='tablinks'>Map of Mull</li>
<li class='tablinks'>Map of Scotland</li>
</ul>
This is why many of us prefer writing jQuery - it's just simpler. And shorter.
An alternative way of doing it is using data attributes...
$('.tablinks').on("click", function() {
var openValue = $(this).data("open");
//openTab(openValue);
console.log(openValue);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<ul id='toptabs' class="tabs" data-persist="true">
<li class='tablinks' data-open="view1" id='default'>How to find Maolbhuidhe</li>
<li class='tablinks' data-open="view2">How to get to Mull</li>
<li class='tablinks' data-open="view3">Map of Mull</li>
<li class='tablinks' data-open="view4">Map of Scotland</li>
</ul>
I'm new here and I'd like to ask a question about jQuery.
I have the unordered list like:
<ul id="pages">
<li id="number1">
contains sub table a
</li>
<li id="number2">
contains sub table b
</li>
<li id="number3">
contains sub table c
</li>
<li id="number4">
contains sub table d
</li>
</ul>
And I'd like to add a different ID (new id will be same out of these id but it will be shuffle as per my requirements) to every li displayed in this <ul> (eg
<li id="number3">contains sub table d</li>
...)
Is there a way how to achieve this via jQuery?
I tried it using a for loop and this object but when i try to shuffle. Its duplicating the sub tables becouse of jQuery has child/nextsybling data inside this every object.
Basically you should get all ids, shuffle them and reassign back to DOM elements:
const $items = $('#pages li');
const ids = $items.map((i, { id }) => id);
const shuffledIds = shuffle(ids); // This is your shuffle function
$items.each((i, e) => {
e.id = shuffledIds[i];
});
I have a list displayed in table where I need to filter the result with first letter of name,above the list I have a letter A B C D and so on.
After click the letter list will be filter by its first name
For ex: list details are Apple Boy Bridge
after click A, Apple will be displayed
Instead of fruit, I had to filter names of countries to display their sales representatives:
'use strict';
angular.module('sodemo')
.filter('firstLetter', function () {
return function (input, letter) {
input = input || [];
var out = [];
input.forEach(function (item) {
//console.log("current item is", item, item.charAt(0));
if (item.charAt(0).toLowerCase() == letter) {
out.push(item);
}
});
return out;
}
});
A quick way to generate an array with letters of the alphabet:
$scope.alphabet = "abcdefghijklmnopqrstuvwxyz".split("");
and the view, which also sets a different background colour if the letter is active:
<button type="button" class="btn-alphabet btn btn-default" ng-repeat="letter in alphabet" ng-click="setActiveLetter(letter)" ng-class="{'btn-primary': letter==activeLetter}">{{letter}}</button>
I filtered elements of the array of countries like this:
<ul class="list-group countries-salesreps" >
<li class="list-group-item" ng-repeat="country in filteredCountriesArray = (countriesArray | firstLetter:activeLetter)" ng-click="showSalesRep(country)" ng-class="{'btn-primary': country==currentCountry}">{{country}}</li>
</ul>
You can check if there are elements in the filtered list using .length:
<div class="alert alert-warning" ng-hide="filteredCountriesArray.length">No available countries starting with <em>{{activeLetter}}</em></div>
So the question has been answered but I came across this looking for an answer and being quite new to angular found it kind of hard to read and understand properly. I then found this tutorial explaining filters and how they work in general and in his examples he creates a 'startsWithLetter' filter which I found quite useful: http://toddmotto.com/everything-about-custom-filters-in-angular-js/
Just thought I would post it in case anyone had trouble understanding like I did.
this is old but maybe this plunker can help, using angular's filter filter.
Define an expression like so:
// Filter Expression
this.filterActive = function(value){
if (self.active) {
return value.charAt(0).toLowerCase() === self.active;
}
return true;
}
Then in html:
<ul>
<li ng-repeat="country in ctrl.countries | filter:ctrl.filterActive" ng-bind="country"></li>
</ul>
<ul>
<li>A</li>
<li>B</li>
<li>C</li>
</ul>
<ul>
<li ng-repeat="name in list | filter:letterFilter">
{{name.firstName}}
</li>
</ul>
try above code this is simple to implement:
I am trying to clone a list item in the DOM and append it multiple times depending on the number of text values in the variable data using the each function. The problem is that all the new list items get the same text values (all list items are set equal to the last appended list item).
I suppose it has something to do with enclosures, but I cannot figure it out.
Can anyone explain what the problem is?
Thanks!
data:
var data = {"text1": ["text1_row1", "text1_row2"], "text2": ["text2_row1", "text2_row2"], "text3": ["text3_row1", "text3_row2"] }
HTML:
<ul>
<li id="entryTemplate" style="display:none">
<span class="text1"></span>
<span class="text2"></span>
<span class="text3"></span>
</li>
</ul>
Javascript:
function listData(data){
$.each(data.text1, function(i) {
var newDataRow = $('#entryTemplate').clone();
newDataRow.removeAttr('id')
.removeAttr('style')
.removeAttr('class')
.addClass('copy')
.appendTo('ul')
.find('.text1').text(data.text1[i])
.find('.text2').text(data.text2[i])
.find('.text3').text(data.text3[i]);
});
}
$.fn.clone = function(){
var ret = $();
this.each(function(){
ret.push(this.cloneNode(true))
});
return ret;
};
Desired HTML:
<ul>
<li id="entryTemplate" style="display:none">
<span class="text1"></span>
<span class="text2"></span>
<span class="text3"></span>
</li>
<li class="copy">
<span class="text1">text1_row1</span>
<span class="text2">text2_row1</span>
<span class="text3">text3_row1</span>
</li>
<li class="copy">
<span class="text1">text1_row2</span>
<span class="text2">text2_row2</span>
<span class="text3">text3_row2</span>
</li>
</ul>
Result I get (all li items get the text for row2):
<ul>
<li id="entryTemplate" style="display:none">
<span class="text1"></span>
<span class="text2"></span>
<span class="text3"></span>
</li>
<li class="copy">
<span class="text1">text1_row2</span>
<span class="text2">text2_row2</span>
<span class="text3">text3_row2</span>
</li>
<li class="copy">
<span class="text1">text1_row2</span>
<span class="text2">text2_row2</span>
<span class="text3">text3_row2</span>
</li>
</ul>
Your question is a bit foggy and it's not really clear what's the result you're looking for. Appart from that I saw something in your code that's probably causing it. Give it a try and let me know what happened.
When you do
$.each(data.text1, function(i) {
change it to
$.each(data.text1, function(pos, element) {
and replace your [ i ] for [element].
According to JQuery API, when doing an EACH loop, the 1st return is the position in the array and the second is the value itself.
UPDATED ANSWER:
Your data object variable is formatted wrong... It looks like you want each value of the data object to be an array (using curly brackets implies you are defining an object with keys), so you should be using square brackets []. See array vs array-like object
var data = {
"text1": ["text1_row1", "text1_row2"],
"text2": ["text2_row1", "text2_row2"],
"text3": ["text3_row1", "text3_row2"]
}
jQuery already has a clone method so you shouldn't need to override with your own.
Based on the data you provided, you want to be iterating over the data variable itself, not data.text1. Then you can get the text values by value of the current index (this is the second parameter passed to .each()) and referencing [0] and [1] to get the actual text
$.each(data,function(i,value){
console.log(value);
var newDataRow = $("#entryTemplate").clone();
newDataRow
.removeAttr('id')
.removeAttr('style')
.removeAttr('class')
.addClass('copy')
.appendTo('ul');
newDataRow.children('.text1').text(value[0]); // value[0] == data[i][0]
newDataRow.children('.text2').text(value[1]);
});