JavaScript function to search elements - javascript

I have a search bar, and an unordered list.
<input onkeyup="Cautare()" type='text' id='userInput' placeholder='Cautare...'>
<button class="Buton" id='Trimitere' onclick="TrimitereWikipedia()">Cautare Wikipedia</button>
<br />
<ul id="myUL">
<li>ISTORIA OPTICII</li>
<li>PRINCIPIILE OPTICII GEOMETRICE</li>
</ul>
With this JS function (Cautare()):
function Cautare() {
var input, filter, ul, li, a, i;
input = document.getElementById("userInput");
filter = input.value.toUpperCase();
ul = document.getElementById("myUL");
li = ul.getElementsByTagName("li");
for (i = 0; i < li.length; i++) {
a = li[i].getElementsByTagName("a")[0];
if (a.innerHTML.toUpperCase().indexOf(filter) > -1) {
li[i].style.display = "";
} else {
li[i].style.display = "none";
}
}}
So this function basically searches what's inside the <a> tags from <li>
I wanted to make this script kinda search what's inside of the content by doing this:
add <p style="display: none">content</p>: so the word 'content' would be another 'keyword' which you can't see;
declaring p variable in the function
p = li[i].getElementsByTagName("p")[0];, under a = li[i].getElementsByTagName("a")[0];
add in the if statement: || p.innerHTML.toUpperCase().indexOf(filter) > -1
So when I put the word "content" in the search bar the first list item should appear, but nothing is happening and in fact all of this breaks the function.
How could I edit the function so I can finish what I want to do? I am not very experienced with JavaScript.

Option 1: data attributes
You can use data attributes to store that additional keyword. Something like <a href="Istoria_Opticii.html" data-keyword="content"> and then you can get that keyword using element.dataset.keyword.
Note that if you want to select those a elements, and you hold reference to their parent, all you need to do is ask for firstChild. And even better, just select them right away using #myUL > li > a pattern combined with querySelectorAll.
Also note that you should use textContent instead of innerHTML if you need to get only the text.
function Cautare() {
const inputValue = document.getElementById('userInput').value.toUpperCase();
const items = [...document.querySelectorAll('#myUL > li > a')];
items.forEach(item => {
if (item.textContent.toUpperCase().includes(inputValue) ||
item.dataset.keyword.toUpperCase().includes(inputValue)) {
item.parentElement.style.display = '';
} else {
item.parentElement.style.display = 'none';
}
});
}
<input onkeyup="Cautare()" type='text' id='userInput' placeholder='Cautare...'>
<!-- <button class="Buton" id='Trimitere' onclick="TrimitereWikipedia()">Cautare Wikipedia</button> -->
<br />
<ul id="myUL">
<li>ISTORIA OPTICII</li>
<li>PRINCIPIILE OPTICII GEOMETRICE</li>
</ul>
Now, if you start typing word content the first li element will stay visible (second will stay visible if you start typing somethingelse).
Option 2: hidden elements
You can use hidden elements the way you have described if you want to. Snippet below shows how, using the hidden p element the way that you have mentioned.
function Cautare() {
const inputValue = document.getElementById('userInput').value.toUpperCase();
const items = [...document.querySelectorAll('#myUL > li')];
items.forEach(item => {
const aElem = item.querySelector('a');
const pElem = item.querySelector('p');
if (aElem.textContent.toUpperCase().includes(inputValue) ||
pElem.textContent.toUpperCase().includes(inputValue)) {
item.style.display = '';
} else {
item.style.display = 'none';
}
});
}
<input onkeyup="Cautare()" type='text' id='userInput' placeholder='Cautare...'>
<!-- <button class="Buton" id='Trimitere' onclick="TrimitereWikipedia()">Cautare Wikipedia</button> -->
<br />
<ul id="myUL">
<li>
<p style="display: none">content</p>
ISTORIA OPTICII
</li>
<li>
<p style="display: none">somethingElse</p>
PRINCIPIILE OPTICII GEOMETRICE
</li>
</ul>
Option 3: Map/WeakMap
Another option would be to create a map that would hold individual li elements as keys and keywords associated with them as values.
function Cautare() {
const inputValue = document.getElementById('userInput').value.toUpperCase();
const items = [...document.querySelectorAll('li')];
const keywords = new WeakMap([[items[0], 'content'], [items[1], 'somethingElse']]);
items.forEach(item => {
if (item.firstChild.textContent.toUpperCase().includes(inputValue) ||
keywords.get(item).toUpperCase().includes(inputValue)) {
item.style.display = '';
} else {
item.style.display = 'none';
}
});
}
<input onkeyup="Cautare()" type='text' id='userInput' placeholder='Cautare...'>
<!-- <button class="Buton" id='Trimitere' onclick="TrimitereWikipedia()">Cautare Wikipedia</button> -->
<br />
<ul id="myUL">
<li>ISTORIA OPTICII</li>
<li>PRINCIPIILE OPTICII GEOMETRICE</li>
</ul>
This approach can be simplified even more if you store both keyword and text content of a given item in the map. In such case, you need to check only one condition.
function Cautare() {
const inputValue = document.getElementById('userInput').value.toUpperCase();
const items = [...document.querySelectorAll('li')];
const keywords = new WeakMap([[items[0], 'content, ISTORIA OPTICII'],
[items[1], 'somethingElse, PRINCIPIILE OPTICII GEOMETRICE']]);
items.forEach(item => {
keywords.get(item).toUpperCase().includes(inputValue) ?
item.style.display = '' :
item.style.display = 'none';
});
}
<input onkeyup="Cautare()" type='text' id='userInput' placeholder='Cautare...'>
<!-- <button class="Buton" id='Trimitere' onclick="TrimitereWikipedia()">Cautare Wikipedia</button> -->
<br />
<ul id="myUL">
<li>ISTORIA OPTICII</li>
<li>PRINCIPIILE OPTICII GEOMETRICE</li>
</ul>

Related

Does JS recursion in for loop breaks after one recursion ends?

I put a main div in it, but for loop broke after one child recursion ends, what should I do to check every children with recusion?
here is output:
direct: DIV main
direct: DIV basicFrame
direct: DIV basicFrame__name
direct: SPAN name
and loop broke after a first recursion ends.
as you can see, after first recursion (main -> basicFrame -> basicFrame__name -> name)
its loop broke (not passed to next children, infoFrame)
output supposed to print these too
direct: DIV infoFrame
direct: SPAN info
..and etc
this is js code
var typeDisplay = async function (target, findType) {
if (findType == "id")
{
target = document.getElementById(target);
target = [target];
}
else if (findType == "direct")
{
console.log("direct: " + target.tagName + " " + target.className); //output maker
target = [target];
}
for (let ind = 0; ind < target.length; ind++)
{
const label = target[ind]; //get label from selection
if (label != null)
{
for (let ind; ind < label.children.length; ind++); //get children of label
{
const subLabel = label.children[ind];
if (typeof(subLabel) == "object")
{
// doing some work
typeDisplay(subLabel, "direct"); //recursion to it's children
}
}
}
}
return;
};
and this is input html form for js script
<div class="main" id="typeTarget"> <!--first parameter of function-->
<div class="basicFrame">
<div class="basicFrame__name">
<span class="name">
wow
</span>
<button class="pen">
<i class="fas fa-file-image"></i>
</button>
</div>
<div class="faceFrame">
<img src="image.png" alt="no image" title="image">
</div>
</div class="infoFrame">
<span class="info">
Info :
<br>
<ul>
<li>this is list</li>
<li>list :
<ul>
<li>a</li>
<li>b</li>
<li>c</li>
</ul>
</li>
</ul>
</span>
</div>
<!--load java script-->
<script>
typeDisplay("typeTarget", "id");
</script>

Classlist validation for unique selected item

So I'm trying to create a chat application like messenger.
When I press the button, a new conversation should be started. I want to add a list item in my overview bar on the left but there can only be one selected, and that one has the 'history-item-selected' classname. So every new convo gets that classname, while the others ones get another classname to change it's appearance but it won't work.
const newConvoButton = document.getElementById("newmessage");
const addNewConvo = (e) => {
e.preventDefault();
const myMessages = document.getElementById('history');
let newListItem = document.createElement('li');
newListItem.textContent = "user " + Math.floor(Math.random(2 - 100) * 100);
myMessages.appendChild(newListItem);
if (newListItem.classList = 'history-item-selected') {
newListItem.classList.add('history-item-selected');
} else {
newListItem.classList.add('history-item')
};
};
newConvoButton.addEventListener('click', addNewConvo);
<main>
<div id="top">
<span>
<h2>My conversations</h2>
</span>
<button type="submit" id="newmessage">+</button>
</div>
<div id="messagecontainer">
<ul id="history"></ul>
<id id="chatscreen">
<ul id="messages">
<li>yolo</li>
</ul>
<div id="messagebottom">
<input type="text" placeholder="Start met typen" size="28" height="auto"> <button type="submit">Send</button>
</div>
</id>
</div>
</main>
There is a "tautological" way to do what you are trying to do.
var addNewConvo = (e)=> {
var nodes = document.querySelectorAll(".history-item-selected");
nodes.forEach(function(elem) {
this.classList.remove("history-item-selected");
});
e.target.classList.add("history-item-selected");
}
In your CSS, you should have something like
.history-item {
/*Styles for history-item*/
}
.history-item.history-item-selected {
/*Styles for elements with both */
}

How to make search filter on entire div

I have HTML code like this in my project
<div class="container" id="containerdiv">
<div id="rollingdiv">
<div class="row">
<div class="col-3" id="columndiv">
<a href="https://www.linkedin.com/in/arun7kumar/">
<div>
<img class="icontrial" src="img/iAngelsmugshotsblackandwhite/ArunKumar.jpeg" alt="">
</div>
<div class="texttrial">
<p>Arun Kumar</p>
<p>Technology</p>
</div>
</a>
</div>
<div class="col-3" id="columndiv">
<a href="https://www.linkedin.com/in/meeraiyer/">
<div>
<img class="icontrial" src="img/iAngelsmugshotsblackandwhite/MeeraIyer.jpeg" alt="">
</div>
<div class="texttrial">
<p>Meera Iyer</p>
<p>Marketing & Communication</p>
</div>
</a>
</div>
<div class="col-3" id="columndiv">
<a href="www.linkedin.com/in/prashant-rohatgi-47b6472">
<div>
<img class="icontrial" src="img/iAngelsmugshotsblackandwhite/PrashantRohtagi.jpeg" alt="">
</div>
<div class="texttrial">
<p>Prashant Rohtagi</p>
<p>Digital Transformation</p>
</div>
</a>
</div>
<div class="col-3" id="columndiv">
<a href="www.linkedin.com/in/richa-arora-05127aa">
<div>
<img class="icontrial" src="img/iAngelsmugshotsblackandwhite/RichaArora.jpeg" alt="">
</div>
<div class="texttrial">
<p>Richa Arora</p>
<p>Sales</p>
</div>
</a>
</div>
</div>
The div=row onwards keeps continuing for about 10 rows. Making 10 rows and 40 such folks with image and name displayed on screen.
I want to make the name which is the first tag in the row searchable. When there is a match, only the div with the id "columndiv" should pop up. So that only the div for the searched person gets displayed.
My function looks like this
<script>
function myFunction() {
var input, filter, ul, li, a, i, txtValue;
var divValues = []
input = document.getElementById('myInput');
filter = input.value.toUpperCase();
ul = document.getElementById("containerdiv");
li = ul.getElementsByTagName('p');
console.log(li)
// Loop through all list items, and hide those who don't match the search query
for (i = 0; i < li.length; i++) {
console.log("hi i am inside")
a=li[i]
txtValue = a.textContent || a.innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
li[i].style.display = "";
} else {
li[i].style.display = "none";
}
}
}
</script>
With the above, only the tag with the name is searchable. However I want the searched div, with the name, image displayed.
How do I go about doing this
You are changing the display value for each p-tag and not for the whole div.
Just run through all DIVs instead of all Ps:
function myFunction() {
var input, filter, ul, li, a, i, txtValue;
input = document.getElementById('myInput');
filter = input.value.toUpperCase();
ul = document.getElementById("containerdiv");
li = ul.getElementsByClassName('col-3'); //<<<<<<<<<<<<<<<< Change this line
// Loop through all list items, and hide those who don't match the search query
for (i = 0; i < li.length; i++) {
a=li[i]
txtValue = a.textContent || a.innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
li[i].style.display = "";
} else {
li[i].style.display = "none";
}
}
}

Remove class if id's the correct ID

Looking to remove a class if a certain button is clicked.
<div class="slide-container">
<section class="about" id="slide-0">
<div class="menu-total">
<nav class="nav">
<button class="nav_link home" onclick="slideTo('slide-2')">HOME</button>
<button class="nav_link about" onclick="slideTo('slide-0')">ABOUT</button>
<button class="nav_link fun-stuff" onclick="slideTo('slide-1')">FUN STUFF</button>
<button class="nav_link professional" onclick="slideTo('slide-3')">PROFESSIONAL</button>
<button class="nav_link contact" onclick="slideTo('slide-4')">CONTACT</button>
</nav>
<div class="hamburger">
<span class="hamburger__patty"></span>
<span class="hamburger__patty"></span>
<span class="hamburger__patty"></span>
</div>
</div>
The one I want to remove the class on is the HOME button. So "slideTo('slide-2)". If it's clicked on the others then the class is kept. I believe someone is either wrong with my loop or not getting the ID correctly of the items/
function slideTo(slideId) {
const slide = document.getElementById(slideId);
slide.scrollIntoView({
behavior: 'smooth'
})
// above this line works fine
let nonHome = document.querySelectorAll('.slide-container section');
let nonHomeID = document.getElementById('slide-2');
var i;
setTimeout(function(){
for (i=0; i < nonHome.length; i++ ){
// i believe it's somewhere here it is wrong
if (nonHome[i].id != nonHomeID){
nonHome[i].classList.add("nav-visibility");
} else{
nonHomeID.classList.remove("nav-visibility");
}
}
}, 1000)
}
If you can use jquery library, you can write in the HTML:
<button class="nav_link" data-value="home">HOME</button>
...
and then in the JS code:
$(".nav_link").on("click", function() {
var valueClicked = $(this).data("value"); // Get the data-value clicked
$(".nav_link").each(function() { // Loop through all elements of the class 'nav-link'
var v = $(this).data("value");
if (v == valueClicked) {
$(this).removeClass("nav-visibility");
} else {
$(this).addClass("nav-visibility");
}
)
}
Not much simpler, but the HTML is cleaner.
Simpler version if it is not required to browse through all buttons at each button click:
$(".nav_link").on("click", function() {
var valueClicked = $(this).data("value"); // The value of the button clicked by the user
if (valueClicked == "home") {
$(this).removeClass("nav-visibility");
console.log('remove')
} else { $(this).addClass("nav-visibility");
console.log('add')
}
});

Changing inner values in a list of items

Let say I have a HTML code:
<li>
<button class="add">+</button>
<span class="amount">0</span>
<button class="substract">-</button>
</li>
<li>
<button class="add">+</button>
<span class="amount">0</span>
<button class="substract">-</button>
</li>
and so on...
and JS:
function addAmount(el) {
let amount = document.querySelector(".amount");
let addAmount = parseInt(amount.innerHTML);
if (el.classList.contains("add")) {
addAmount = addAmount + 1;
amount.innerHTML = addAmount;
}
return addAmount;
}
document.addEventListener("click", e => {
addAmount(e.target);
});
This code works only for first li element. I would like to know how I can obtain a code in which every button from each li element is responsible for only one element (one button adds value for one li element).
You shouldn't be searching the document at all. You can use document.querySelectorAll(".amount") to get a list of all the amount fields, but you need to figure out which index to use.
Instead, you can use previousElementSibling or nextElementSibling to get the element before or after the button you clicked on.
function addAmount(el) {
let amountElement, adjustAmount
if (el.classList.contains("add")) {
adjustAmount = 1;
amountElement = el.nextElementSibling;
} else {
adjustAmount = -1;
amountElement = el.previousElementSibling;
}
amountElement.innerText = parseInt(amountElement.innerText) + adjustAmount;
}
document.addEventListener("click", e => {
addAmount(e.target);
});
<li>
<button class="add">+</button>
<span class="amount">0</span>
<button class="substract">-</button>
</li>
<li>
<button class="add">+</button>
<span class="amount">0</span>
<button class="substract">-</button>
</li>

Categories