jQuery search for paragraphs containing multiple words - javascript

I have an unordered list called test
<ul id='test'></ul>
it is dynamically populated with data via ajax. Each item 'li' is a div containing 'p' paragraphs. Each paragraph contains some information.
Ex:
<li> <div> <p> test </p> </div> </li>
<li> <div> <p> hi how is it going?</p> </div> </li>
<li> <div> <p> not a test</p> </div> </li>
<li> <div> <p> whoa</p> </div> </li>
I also have a search box which i can get a search term from, I use:
var searchTerm = $("#search").val().trim().split(' '); // an array of words searched for
What I am trying to do is find a way to select all 'li' elements which contain all or some of the search words, but I'm not sure how to approach it best.
Currently, I am doing this:
var results = $('p:contains("'+ searchTerm[0] +'")');
to get an exact match on the first term, but I want to search for multiple terms, not just one.
I would want to search for 'test hi' and get back three nodes cause it searches for 'test' and 'hi'.
I also thought of:
var results2 = $('p').filter(function( index ) {
return ( this +':contains("'+ searchTerm +'")' );
});
Anyone point me in the right direction?

You could do some black magic with the selector, like this:
var results = $('p:contains("' + searchTerm.join('"), p:contains("') + '")');
This looks hard, but I'll explain it.
It joins the search terms with "), p:contains(". Then it just adds the missing p:contains(" and ") to the ends of the result string and searches for it.

A combination of $.filter and $.each (or array.forEach, if you don't care about ie8) can also be of use here:
var searchTerms = ["how", "test"];
$('div').filter(function () {
$text = $(this).text();
var found = 0;
$.each(searchTerms, function (index, term) {
found = $text.indexOf(term) > -1 ? found +1 : found;
})
return found;
}).addClass('match');
jsFiddle

Related

Jquery - Get First character of word after space

Used below HTML & JS to get first character of string and display. Manually its possible to calculate and get value.
When its goes to dynamic and name with only first and last name not sure how to calculate character position after space and get first character of word.
$('.splitname .fname').html(name.charAt(0));
$('.splitname .mname').html(name.charAt(8));
$('.splitname .lname').html(name.charAt(16));
<div class="name">Desmond Patrick Reymond</div>
<div class="splitname">
<span class="fname">D</span>
<span class="mname">P</span>
<span class="lname">R</span>
</div>
Use this logic:
"Desmond Patrick Reymond".split(" ").map(name => name[0])
// => ["D", "P", "R"]
If you need to modify the HTML programmatically, do:
let s = $('.name').text();
s.split(" ").map(name => $('.splitname').append(name[0]))
(It's not really good practice to use map for side effects though; you may choose to use forEach instead.)
You can simply use match() function and a simple ReGex to get the dynamic text data with spaces without having to check for charAt()
//get text
let name = $('.name').text();
//match the spaces
var matches = name.match(/\b(\w)/g); // [D,P,R]
//join the chars - if needed
var joinChar = matches.join(''); // DPR
//show the split name
$('.splitname').text(joinChar);
console.log(matches ) // [D,P,R] //Array
console.log(joinChar) //DPR //String
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="name">Desmond Patrick Reymond</div>
<div class="splitname"></div>
You can make use of Array#split and Array#map and Array#join as in the demo below. Result will be:
<div class="splitname">
<span class="fname">D</span>
<span class="mname">P</span>
<span class="lname">R</span>
</div>
//Classes for the initials
const classes = ['fname', 'mname', 'lname'];
//Where to put the initials
$('.splitname')
//make HTML generated content of
.html(
//Get Full Name
$('.name').text()
//Break into names array
.split(' ')
//Get initial of each name
.map(name => name.charAt(0))
//Wrap each initial in a span element
.map((initial,index) => `<span class="${classes[index]}">${initial}</span>`)
//Join all span elements array into string
.join('')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="name">Desmond Patrick Reymond</div>
<div class="splitname"></div>

Selecting a childnode by inner text from a NodeList

First part [solved]
Given this HTML
<div id="search_filters">
<section class="facet clearfix"><p>something</p><ul>...</ul></section>
<section class="facet clearfix"><p>something1</p><ul>...</ul></section>
<section class="facet clearfix"><p>something2</p><ul>...</ul></section>
<section class="facet clearfix"><p>something3</p><ul>...</ul></section>
<section class="facet clearfix"><p>something4</p><ul>...</ul></section>
</div>
I can select all the section with
const select = document.querySelectorAll('section[class^="facet clearfix"]');
The result is a nodelist with 5 children.
What I'm trying to accomplish is to select only the section containing the "something3" string.
This is my first attempt:
`const xpathResult = document.evaluate( //section[p[contains(.,'something3')]],
select, null, XPathResult.ANY_TYPE, null );
How can I filter the selection to select only the node I need?
Second part:
Sorry for keeping update the question but it seems this is a problem out of my actual skill..
Now that i get the Node i need to work in what i've to do it's to set a custom order of the li in the sections:
<ul id="" class="collapse">
<li>
<label>
<span>
<input>
<span>
<a> TEXT
</a>
</span>
</input>
</span>
</label>
</li>
<li>..</li>
Assuming there are n with n different texts and i've a custom orders to follow i think the best way to go it would be look at the innertex and matching with an array where i set the correct order.
var sorOrder = ['text2','text1','text4','text3']
I think this approach should lead you to the solution.
Giving your HTML
<div id="search_filters">
<section class="facet clearfix"><p>something</p><ul>...</ul></section>
<section class="facet clearfix"><p>something1</p><ul>...</ul></section>
<section class="facet clearfix"><p>something2</p><ul>...</ul></section>
<section class="facet clearfix"><p>something3</p><ul>...</ul></section>
<section class="facet clearfix"><p>something4</p><ul>...</ul></section>
</div>
I would write this js
const needle = "something3";
const selection = document.querySelectorAll('section.facet.clearfix');
let i = -1;
console.info("SELECTION", selection);
let targetIndex;
while(++i < selection.length){
if(selection[i].innerHTML.indexOf(needle) > -1){
targetIndex = i;
}
}
console.info("targetIndex", targetIndex);
console.info("TARGET", selection[targetIndex]);
Then you can play and swap elements around without removing them from the DOM.
PS. Since you know the CSS classes for the elements you don't need to use the ^* (start with) selector. I also improved that.
PART 2: ordering children li based on content
const ul = selection[targetIndex].querySelector("ul"); // Find the <ul>
const lis = ul.querySelectorAll("li"); // Find all the <li>
const sortOrder = ['text2', 'text1', 'text4', 'text3'];
i = -1; // We already declared this above
while(++i < sortOrder.length){
const li = [...lis].find((li) => li.innerHTML.indexOf(sortOrder[i]) > -1);
!!li && ul.appendChild(li);
}
This will move the elements you want (only the one listed in sortOrder) in the order you need, based on the content and the position in sortOrder.
Codepen Here
In order to use a higher order function like filter on your nodelist, you have to change it to an array. You can do this by destructering:
var select = document.querySelectorAll('section[class^="facet clearfix"]');
const filtered = [...select].filter(section => section.children[0].innerText == "something3")
This answer explains the magic behind it better.
You can get that list filtered as:
DOM Elements:
Array.from(document.querySelectorAll('section[class^="facet clearfix"]')).filter(_ => _.querySelector('p').innerText === "something3")
HTML Strings:
Array.from(document.querySelectorAll('section[class^="facet clearfix"]')).filter(_ => _.querySelector('p').innerText === "something3").map(_ => _.outerHTML)
:)

My filter function is not registering anything with a & in javascript

I have created a simple program that filters words based on a list from an external txt file with regex inside my js file.
For the most part when I input a list of words from the txt file they are filtered out. However there is one keyword that is causing me trouble.
'black & decker'
I am guessing it has to do with the '&' sign because any new words I put into the txt file with a '&' sign does not filter out for some reason.
Can anyone help me to why words with the & is not filtering out properly? And also check if my regex is written properly for this program(var filtered)?
Any help would be much appreciated. Thank you.
I have a list of words in a txt file here:
baby bullet, baby-bullet, back2life, back-2-life, black & decker, black-decker, black-&-decker, britax, bose, capital brands products, capital-brands-products, dewalt, dyson, ergobaby, ergo-baby, fiskars, ickle bubba, ickle-bubba, kitchen aid, kitchen-aid, longstem, long-stem, magic bullet, magic-bullet, makita tools, makita-tools, milwaukee, monster cable, monster-cable, mustee, nest, nutri____, nutribullet, oxo, party bullet, shark, simplehuman, sony bravia, urban decay, urban-decay, waterpik, weber grill, weber-grill, youthology, teeter
Here is my JS file that filters inputted words based on the txt list
// This grabs the data from the txt file and splits each word by commas
$.get('word-list.txt', function(data) {
pbfFilterWords = data.split(', ');
pbfFilterWords.forEach(function(word){
pbfWordList = word;
});
// This defines a global variable so the filter button has a list of words to filter with
var pbfWordList = pbfFilterWords;
//This will allow the list of words to filter with regex
var pbfRegEx = pbfFilterWords;
var filtered = (function(){
var filtered = [], i = pbfRegEx.length;
while (i--) {
if (/w*[\s|\w|\b+]*\w*/.test(pbfRegEx[i])) {
filtered.push(pbfRegEx[i]);
}
}
return filtered;
})();
console.log(filtered.join());
// Function for filter button
$('.pbf-link-container[contenteditable]').html;
$('#pbf-filter').click(function(){
var $pbfOutput = $('.pbf-link-container[contenteditable]').html();
// Array of words for filter
var pbfFilterWords = pbfWordList;
// Output to new DIV and remove specified keywords from pbfFilterWords
$('.pbf-link-output').html($pbfOutput);
// To make pbfFilterWords not case sensitive
$.expr[":"].contains = $.expr.createPseudo(function(arg) {
return function( elem ) {
return $(elem).html().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
};
});
// Function to output the filtered words
$.each(pbfFilterWords , function(i , filtered){
$('.pbf-link-output > div:contains("'+filtered+'")').remove();
});
});
});
Here is my html:
<div id="pbf-container">
.....
<div class="pbf-link-container" contenteditable="true">
<div><br/></div>
</div>
<div class="pbf-button-control">
<button id="pbf-filter"> Filter </button>
</div>
<div class="pbf-filter-header">
<h3> Filtered Links </h3>
</div>
<div class="pbf-link-output">
</div>
</div>
Your pseudo selector implementation takes html version of inner contents of the div tag, so it converts & to &AMP; and you don't have a match.
You can see yourself:
$.expr[":"].contains = $.expr.createPseudo(function(arg) {
return function( elem ) {
// console.log value of current element here
console.log($(elem).html().toUpperCase()); // logs string with &AMP;
return $(elem).html().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
};
});
use $(elem).text() to get text version of inner content of a div.

Jquery - $(this) id from selector group

So
I've defined a simple list and an output
<div class="item-list">
<p id="One">First Item</p>
<p id="Two">Second Item</p>
<p id="Three">Third Item</p>
</div>
<div class="box">Add Stuff in Here!</div>
And
I've written this script
$(".item-list > p").click(function(){
var thisId = (this.id);
$(".box").append('<p>' + thisId + '</p>');
});
I want
each item, on click, to append its id as text inside .box element
Like So
<div class="box>One</div>
And this works
just like you'd expect. JS Fiddle of the simplified Working Example.
But in my more complex example:
the id of the selected element
(this.id)
comes back as ...
Ew..
undefined.
Here's a JS Fiddle of the more complex version of this:
http://jsfiddle.net/pQz3W/4/
What's causing these id's to return undefined?
To be clear, In the more complex example, I'm doing the same thing, it's just crowded by other functionality. Something in there is causing my id to come back as undefined.
I'll keep posting updates to this fiddle as I clean the code up more and more to pertain only to what's necessary to be there for this question!
You have an array
ServiceArray = [
[
["SplashPage", "Splash Page", "hey!"],
["GamingWebsite", "Gaming Website", "yo!"],
["BasicWebsite", "Basic Website", "hi!"],
["AdvancedWebsite", "Advanced Website", "ooh!"],
["FlexWebsite", "Flex Website", "ahh!"],
......etc
and you have ID's that you're trying to look up in that array like this
var thisId = this.id;
var outputList = ServiceArray[0][thisId][2];
but there are no associative arrays in javascript, and the keys you're looking for doesn't exist. You need to rebuild that array with objects that has keys and values that actually match what you're looking for.
try this: var thisId = $(this).attr("id");

Best way to iterate through a 'sortable' list and then change the values accordingly

I have a list of items in a list:
<ul id="myList">
<li>10 Apple</li>
<li>20 Banana</li>
<li>30 Orange</li>
</ul>
The list can be sorted thanks to jQuery. However once Ive arranged the items i need to recalculate their positions i.e Orange moved to top would equal 10, apple would then equal 20 and Banana 30.
I then need to pass this data in the url in order for my server side script to process.
What is the best way to be able to pass the altered list values into the URL when i submit?
My first thought was to have a form with hidden values, so when i process the list it adds the values into the form as hidden inputs in order for me to pass the data into the URL.
Is the number before each item strictly based on position?
If so, once the sort is complete you could just replace the content based on each li element.
Does it matter how the URL contains the list text?
In the below I operated with the assumption that it can all be appended with a querystring value of list.
There are shorter ways to do this, but hopefully the verbosity will aid in understanding what happens.
<script>
var listChanger = function() {
//updates the digits prepended to each item in list
//updates URL with text of list in querystring
//accumulates the text of each list item
var valueAccumulator = [];
//Operate of each item in myList
$('#myList li').each(function(index) {
//regular expression to strip initial digits
var reInitialDigits = /^\d+/;
//remove initial digits
var textWithoutDigits = $(this).text().replace(reInitialDigits,"");
//prepend new digits (index starts at 0, so add 1 then multiply by 10 to get desired number)
$(this).text(((index + 1) * 10) + textWithoutDigits);
//append text to accumulator
valueAccumulator.push($(this).text());
});
//querystring to strip everything after the querystring
var reQueryString = /\?list\=.*$/;
//placeholder
var linkWithoutQueryString = '';
//remove querystring
linkWithoutQueryString = $('#myLink').attr('href').replace(reQueryString,'');
//change link to include URL encoded list of values
$('#myLink').attr('href',linkWithoutQueryString + "?list=" + encodeURI(valueAccumulator.join("|")));
}
$(document).ready(function() {
//make the list sortable and each time the sorting stops, run the listChanger function
$("#myList").sortable({stop:listChanger});
});
</script>
<ul id="myList">
<li>10 Apple
</li>
<li>20 Banana
</li>
<li>30 Orange
</li>
</ul>
<br />
<a id="myLink" href="mylink.html">mylink.html</a>

Categories