How to display search results onkeyup? - javascript

I have a search box that as the user types letters into the search box, we will filter and display the results. However, as the user types each letter the search results are getting toggled between showing and hiding. I am very new to JS so I hope it could be an easy fix.
Here is my HTML:
See Below
Here is my toggle JS:
See Below
How can I tweak the JS to not toggle back and forth?
//Here are my edits to help answer the question. This is the JS and HTML I am using to display the results:
HTML:
<div class="friendssearch" onclick="toggle_visibility('friendslist');">
<div class="friendssearch">
<div id="friendssearchbox"></div>
</div>
<ul id="friendslist" style="display: none;">
<li>
<a href="#">
<div class="friendsflybox" title="Friends Name">
<p class="friendsflyboxname">Ryan Bennett</p>
</div>
</a>
</li>
</ul>
</div>
Javascript:
<script>
(function ($) {
// custom css expression for a case-insensitive contains()
jQuery.expr[':'].Contains = function(a,i,m){
return (a.textContent || a.innerText ||
"").toUpperCase().indexOf(m[3].toUpperCase())>=0;
};
function listFilter(header, list) { // header is any element, list is an unordered list
// create and add the filter form to the header
var form = $("<form>").attr({"class":"filterform","action":"#"}),
input = $("<input>").attr({"class":"filterinput clearFieldBlurred
ClearField","type":"text", "value":"Start typing a Name"});
$(form).append(input).appendTo(header);
$(document).ready(function() {
$('.clearField').clearField();
});
$(input)
.change( function () {
var filter = $(this).val();
if(filter) {
// this finds all links in a list that contain the input,
// and hide the ones not containing the input while showing the ones that do
$(list).find("a:not(:Contains(" + filter + "))").parent().slideUp();
$(list).find("a:Contains(" + filter + ")").parent().slideDown();
} else {
$(list).find("li").slideDown();
}
return false;
})
.keyup( function () {
// fire the above change event after every letter
$(this).change();
});
}
//ondomready
$(function () {
listFilter($("#friendssearchbox"), $("#friendslist"));
});
}(jQuery));
</script>

You'll need to do something similar to the code I have posted below. This assumes that you can access the object that contains the search results.
function toggle_visibility(id)
{
//Check if there are any search results to display
var searchResultLength = document.getElementById(searchResultID).innerHTML.length;
if (searchResultLength > 0) // display div
{
var e = document.getElementById(id);
e.style.display = 'block';
}
else //No search results, hide div
{
e.style.display = 'none';
}
}
Basically, you need to determine if you have search results to display before you attempt to toggle the div's visibility.
//EDIT AFTER COMMENTS
OK, so it looks like the results are adding li's to the ul. So, assuming that the code is taking away the li's as well as adding them, you should be checking for the number of elements in the ul == 0. See below.
$('#friendslist > li').length
To be honest, I'm having a bit of difficulty trying to determine exaclty what the code is
doing. I'm certainly not a jquery expert. I would say if the above code doesn't get you going in the right direction, I'm out of ideas.

If you're only wanting it to display when you enter the field use the onFocus="method()" attribute. followed by onBlur="method()". this will display the block when you enter the field and hide it when you leave.
<input id="searchbox" type="text" onFocus="toggle_visibility('friendslist');" onBlur="toggle_visibility('friendslist')">
<ul id="friendslist" style="display: none;">
<!--search results HTML-->
</ul>
teach a man to fish: http://www.w3schools.com/jsref/dom_obj_event.asp
// EDIT
I think Quickfire's answer is the best solution. but as I understand it you want you results to show/hide, so I modified his method to better suit your markup.
function toggle_visibility(id){
//Get the total number of <li> within my search result
var results=document.getElementById(searchResultID).childNodes.length;
if (results > 0){ // we have more than 0 results
var e = document.getElementById(id);
e.style.display = 'block'; // show the element
}else{ //No search results, hide div
e.style.display = 'none'; //hide the element
}
}

Related

If container has children of a certain class

I'm working on a live search function, but I need to hide any containers that do not have a match.
This is a sample of how the containers are populated if nothing is entered in the search bar. EDIT: This will be identical even if there is a string in the search bar. fadeOut() hides the element, doesn't remove it.
<div id="Alabama_container" class="state-container">
<h1>Alabama</h1>
<div id="330_store-object" class="store-object" style="">
<h4>Store 330 - Birmingham</h4>
<p>(205) 981-1320</p>
<p>5201 US-280, Birmingham, AL 35242, USA</p>
<button id="330_store-object-link" class="button">View on Map</button><button id="330_store-object-floorPlan" class="button">Floorplans</button>
</div>
<div id="337_store-object" class="store-object" style="">
<h4>Store 337 - Dothan</h4>
<p>(334) 671-1370</p>
<p>4401 Montgomery Hwy #300, Dothan, AL 36303, USA</p>
<button id="337_store-object-link" class="button">View on Map</button><button id="337_store-object-floorPlan" class="button">Floorplans</button>
</div>
</div>
The state-container elements are generated on the DOM first then store-object elements are appended to the appropriately named state-container.
Search Function Sample:
$(document).ready(function () {
$("#store-search").keyup(function () {
var filter = $(this).val(),
count = 0;
$(".store-object").each(function () {
// If the store object doesn't match, remove it
if ($(this).text().search(new RegExp(filter, "i")) < 0) {
$(this).fadeOut();
// Show the store objects that do match the query
} else {
$(this).show();
count++;
}
});
// Results counter for troubleshooting
var numberItems = count;
$("#filter-count").text(count + "Results Founds");
});
});
My search function simply uses filter to determine any matching strings within the store-object elements and hides any that don't match. However, the matches still reside in their state-container so what you end up with is a list of states without any results inside.
What I want to do is loop through state-container elements and determine if it contains any store-object children so I can handle it appropriately. What would I use to achieve this?
There are several different ways to achieve what you want. One of which would be using the :visible selector and then hiding the state-container on an empty set.
$('.state-container').each(function(){
if($(this).find('.store-object:visible').length === 0){
$(this).hide();
}
});
Keep in mind, you'll need to run that after all of the children's fadeOut animations have completed.
Another approach would be to keep a tally of the hidden elements as you go through the search, and if all children were hidden remove the parent.
$(document).ready(function () {
$('.state-container').each(function(){
$(this).data('total', $(this).find('.store-object').length);//set a count of total
});
$("#store-search").keyup(function () {
var filter = $(this).val(),
count = 0;
//reset the hidden count for the states
$('.state-container').each(function(){
$(this).data('hidden', 0);//initialize to 0
$(this).show();
});
$(".store-object").each(function () {
var parent = $(this).parent();
// If the store object doesn't match, remove it
if ($(this).text().search(new RegExp(filter, "i")) < 0) {
$(this).fadeOut();
parent.data('hidden', parent.data('hidden') + 1);//increment hidden count
// Show the store objects that do match the query
} else {
$(this).show();
count++;
}
if(parent.data('hidden') == parent.data('total')){
parent.hide();
}
});
// Results counter for troubleshooting
var numberItems = count;
$("#filter-count").text(count + "Results Founds");
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id='store-search' />
<div id="Alabama_container" class="state-container">
<h1>Alabama</h1>
<div id="330_store-object" class="store-object" style="">
<h4>Store 330 - Birmingham</h4>
<p>(205) 981-1320</p>
<p>5201 US-280, Birmingham, AL 35242, USA</p>
<button id="330_store-object-link" class="button">View on Map</button><button id="330_store-object-floorPlan" class="button">Floorplans</button>
</div>
<div id="337_store-object" class="store-object" style="">
<h4>Store 337 - Dothan</h4>
<p>(334) 671-1370</p>
<p>4401 Montgomery Hwy #300, Dothan, AL 36303, USA</p>
<button id="337_store-object-link" class="button">View on Map</button><button id="337_store-object-floorPlan" class="button">Floorplans</button>
</div>
</div>
You can check if store-container has store-object and then only loop through them like:
$(".store-container").find(".store-object").length &&
$(".store-object").each(function () {
After your edit, I could say that you should check for visible length like:
$(".state-container").find(".store-object:visible").length &&
But, I would still say that you don't need to check anything because you're already checking for the condition that if it's visible then only fadeOut. That is, even if this is hidden, there's nothing problem using fadeOut. But what I can say is that you can pause the search execution:
setTimeout(()=>{
// your each function
}, 600);

Javascript live search not responding?

Trying to create a live search using javascript and it seems as if the script is not responding. I have the script within a .js file and I have added it at the end of the page.
The functionality I want is when the user types into #PokemonSearch it will hid irrelevant pokemon-selector-item divs
What happens when I type into #PokemonSearch with the following code? Absolutely nothing.
$(document).ready(function(){
$("#PokemonSearch").keyup(function(){
// Retrieve the input field text and reset the count to zero
var filter = $(this).val(), count = 0;
// Loop through the comment list
$(".pokemon-selector-item").each(function(){
// If the list item does not contain the text phrase fade it out
if ($(this).attr(".pokemon-name").text().search(new RegExp(filter, "i")) < 0) {
$(this).fadeOut();
// Show the list item if the phrase matches and increase the count by 1
} else {
$(this).show();
count++;
}
});
});
});
HTML Selector item example:
<div class="pokemon-selector-item">
<img class="pokemon-image" src="~/assets/images/pokemon/pokemon-50x50/charmander.png" />
<div class="pokemon-selector-info">
<span class="pokemon-name">Charmander</span>
<span class="pokemon-type">Fire Pokemon</span>
</div>
</div>
You have a typo. .attr(".pokemon-name") on line 11 should be .find(".pokemon-name")

How can I automatically merge unnecessary html tags

How can I merge a tags with the same url together if they are beside each other. For instance, I am dealing with html that looks similar to this:
<div>
<a href='/url.com'>This is</a><a href='/url.com'> the </a><a href='/url.com'>same link.</a>
This is not linked but might have some <b>bolding</b> or not.
<a href='/url.com'>These are</a><a href='/url2.com'> two different links.</a>
</div>
Through jQuery, I would like the inside of the div to be:
<div>
<a href='/url.com'>This is the same link.</a>
This is not linked but might have some <b>bolding</b> or not.
<a href='/url.com'>These are</a><a href='/url2.com'> two different links.</a>
</div>
I can merge tags together by iterating through each pair of a tags to see if they share (1) the same link and (2) the same parent, but then I get output like this:
<div>
<a href='/url.com'>This is the same link.These are</a>
This is not linked but might have some <b>bolding</b> or not.
<a href='/url2.com'> two different links.</a>
</div>
I'm not sure how to realize that there's text in the middle.
EDIT: Here's what I've tried
$('a').addClass('linkCheck');
while ($('.linkCheck').length > 0) {
first_to_check = $('.linkCheck:first');
first_to_check.removeClass('linkCheck');
if ($('.linkCheck').length > 0) {
second_to_check = $('.linkCheck:first');
replaced = false;
if (first_to_check.attr('href') == second_to_check.attr('href')) {
found_first = false;
old_content_html = $('#divID').html();
old_content_text = $('#divID').text();
first_to_check.parent().children().each(function () {
if (found_first == true && $(this).get(0) == second_to_check.get(0)) {
html = second_to_check.html();
second_to_check.remove();
first_to_check.html(first_to_check.html() + html);
found_first = false;
replaced = true;
if ($('#divID').text() != old_content_text) {
replaced = false;
$('#divID').html(old_content_html);
}
return false;
}
if ($(this).get(0) == first_to_check.get(0)) {
found_first = true;
}
else {
found_first = false;
}
});
}
if (replaced == true) {
first_to_check.addClass('linkCheck');
}
}
}
For each anchor, see if its next sibling is an anchor with the same href. If so, append its sibling's contents() and remove the sibling.
You'll need to use the DOM nextSibling to handle text nodes correctly, because jQuery's next() method skips over them.
Repeat as long as duplicate adjacent hrefs are found:
function merge() {
var merged;
do {
merged = false;
$('a').each(function() {
var nexta = $(this.nextSibling);
if (nexta.attr('href') === $(this).attr('href')) {
$(this).append(nexta.contents());
merged = true;
nexta.remove();
}
});
} while (merged);
} //merge
merge();
$('pre').text($('div').html());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<a href='/url.com'>This is</a><a href='/url.com'> the </a><a href='/url.com'>same link.</a>
This is not linked and has <b>bolding</b>.
<a href='/url.com'>These are</a><a href='/url2.com'> two different links.</a>
<br>
<a href='/url.com'>This is</a><a href='/url.com'> the </a><a href='/url.com'>same link.</a>
This is not linked and does not have bolding.
<a href='/url.com'>These are</a><a href='/url2.com'> two different links.</a>
</div>
<hr>
Output:
<pre></pre>
It may seem like this will be a simple operation but it won't. The reason being is that the text in the middle is not a node of it's own but instead just the innerHTML of the parent DIV. Basically what you will need to do is get your tags by tag name, and in order to check if they are adjacent to one another use the following code.
var linkTags=document.getElementsByTagName("A");
for(i=0; i<linkTags.length-1; i++){
j=i+1;
indexOfI=document.innerHTML.indexOf(linkTags[i].outerHTML);
lengthOfI=linkTags[i].outerHTML.length;
indexOfJ=document.innerHTML.indexOf(linkTags[j].outerHTML);
if(indexOfI+lengthOfI==indexOfJ){
//ELEMENTS ARE ADJACENT
}else{
testFlag=true;
//GET CONTENTS BETWEEN ELEMENTS AS STRING
var testString=document.innerHTML.substr(indexOfI+lengthOfI, indexOfJ);
//TEST CONTENTS FOR WHITESPACE
for(k=0; k<testString.length; k++;){
if(testString.char(k)!=" " && testString.char(k)!="\n"){
//SET FLAG INDICATING NON WHITESPACE CHARACTER FOUND
testFlag=false;
//SET K TO LOOPS MAX TO BREAK LOOP
k=testString.length;
}
}
if(testFlag){
//ELEMENTS ARE ADJACENTWITH WHITESPACE BETWEEN
}
}
}
Of course with this adjacency check you are automatically assured that they are of the same parent as well as there is no way to open or close the parent tags and still remain adjacent in the code.
You can try this... Add a class for which anchors need to be verified. $.each through all of the <a> tags. filtering the outstanding items that haven't been verified to see if the href and html() matches. If they match remove them. Then remove the verify class from the item so that on the next each loop the items that have been verified aren't included.
$('a').addClass('verify').each(function () {
var $this = $(this);
//you can change the html() to text() is you are only worried about match the innerText
$('a.verify').not(this).filter(function () {
return ($(this).html() == $this.html() && $(this).attr('href') == $this.attr('href'))
}).remove();
$this.removeClass('verify')
});

Get all IDs of selected/highlighted DIVs

I may be going in the completely wrong direction with what I'm trying to do, so I wanted to ask for help.
Background / Overview
I need to display a paragraph of text and allow a user to select one or more words from the paragraph and save their highlighted text to a database, for just their profile. Actually, hat selection of text will eventually be (1) stored with the highlight AND (2) linked up to another set of highlighted text from another paragraph (basically, I'm tying a phrase from one source to a reference source)
What I've tried...
I have tried to put each word of the paragraph into a DIV (and a unique ID) with each DIV set to float left, so that the display looks okay.
<style>
div { float: left}
</style>
and...using an example:
<div id="GEN_1_1">
<div id="GEN_1_1_1">In</div>
<div id="GEN_1_1_2">the</div>
<div id="GEN_1_1_3">beginning</div>
<div id="GEN_1_1_4">God</div>
<div id="GEN_1_1_5">created</div>
<div id="GEN_1_1_6">the</div>
<div id="GEN_1_1_7">heaven</div>
<div id="GEN_1_1_8">and</div>
<div id="GEN_1_1_9">the</div>
<div id="GEN_1_1_10">earth</div>.
</div>
Which looks like: In the beginning God created the heaven and the earth. (minus the bold)
So far, I have used the
window.getSelection()
function to determine/grab the words that have been highlighted.
I then tried using this:
if (window.getSelection)
{
selected_len = window.getSelection().toString().length;
if (window.getSelection().toString().length>0)
{
div_id = window.getSelection().getRangeAt(0).commonAncestorContainer.parentNode.id;
}
}
to get the ID's for each DIV selected, BUT I only get a single DIV ID returned right now.
Help Request:
Is there are slick way to get the ID for each DIV selected and put it into an Array, so that I can construct a SQL query to put it into the database (the query is easy)? The selected words could total up to several hundred, if not a thousand words, so I need to make sure the solution will work with a ton of words selected.
UPDATE: JSFIDDLE DEMO
I modified the code again. See if it works for you now.
$(document).ready(function () {
$(document).on('mouseup keyup', '.checked', function () {
console.log(window.getSelection());
if (window.getSelection().toString().length>0) {
var count = window.getSelection();
var arr = [];
$('.checked span').each(function(){
var span = $(this)[0];
var isT = window.getSelection().containsNode(span, true);
if(isT){
arr.push($(this).attr('id'));
}
});
console.log(arr,count.toString());
alert(arr);
alert(count.toString());
}
});
});
I created a fiddle for solution. Check it out here: http://jsfiddle.net/lotusgodkk/GCu2D/18/
Also, I used span instead of div for the text selection. I hope that won't be an issue for you. So the code works as you want. It will return the id of the parent span in which text is selected. You can modify it to save the ID into array or as per your needs.
$(document).ready(function () {
$(document).on('mouseup', '.checked', function () {
if (window.getSelection) {
var i = getSelectionParentElement();
console.log(i);
alert('parent selected: ' + i.id);
}
});
});
function getSelectionParentElement() {
var parent = null, selection;
if (window.getSelection) {
selection = window.getSelection();
if (selection.rangeCount) {
parent = selection.getRangeAt(0).commonAncestorContainer;
if (parent.nodeType != 1) {
parent = parent.parentNode;
}
}
} else if ((selection = document.selection) && selection.type != "Control") {
parent = selection.createRange().parentElement();
}
return parent;
}

tag cloud filter

I have a div that contains many spans and each of those spans contains a single href.
Basically it's a tag cloud. What I'd like to do is have a textbox that filters the tag cloud on KeyUp event.
Any ideas or is this possible?
Updated question: What would be the best way to reset the list to start the search over again?
Basically, what you want to do is something like this
$('#myTextbox').keyup(function() {
$('#divCloud > span').not('span:contains(' + $(this).val() + ')').hide();
});
This can probably be improved upon and made lighter but this at least gives the functionality of being able to hide multiple tags by seperating your input by commas. For example: entering this, that, something into the input will hide each of those spans.
Demo HTML:
<div id="tag_cloud">
<span>this</span>
<span>that</span>
<span>another</span>
<span>something</span>
<span>random</span>
</div>
<input type="text" id="filter" />
Demo jQuery:
function oc(a){
var o = {};
for(var i=0;i<a.length;i++){
o[a[i]]='';
}
return o;
}
$(function(){
$('#filter').keyup(function(){
var a = this.value.replace(/ /g,'').split(',');
$('#tag_cloud span').each(function(){
if($(this).text() in oc(a)){
$(this).hide();
}
else {
$(this).show();
}
})
})
})

Categories