jQuery - show list items that match data-attribute - javascript

The function for finding the active menu option and showing the li with the matching data-type only works if the active class is on the first or second li. If I put the active class on the third or fourth li, no items show for either table.
How do I show all of the items with the data-type that matches the data-related value with the active class?
Note the click function works fine, the issue is showing the li for the active selection on load.
$(document).ready(function() {
//Retreive active list on load:::::::::::::::::::::::::::::::
$("[data-section='table']").each(function() {
var $container = $(this);
var item = $container.find(".billing__item");
var active = $container.find(".ladder__ul li.active");
var $id = item.attr("data-type");
var datarel = active.attr("data-related");
item.hide();
// Find out what Li has the active class, then return matching items on load:
if (datarel == "all") {
item.css("display", "flex");
}
if (datarel == $id) {
$(".billing__item[data-type='" + $id + "']").css("display", "flex");
}
});
//Retreive active list on Click:::::::::::::::::::::::::::::::
$(".ladder__ul li").on("click", function(e) {
e.preventDefault();
var $li = $(this);
var $table = $li.closest("[data-section='table']");
var $id = $li.attr("data-related");
$(".ladder__ul li.active").removeClass("active");
$(this).addClass("active");
$table.find(".billing__item").each(function() {
var $item = $(this);
var $type = $item.attr("data-type");
//Hide every list on load::
$item.hide();
if ($li.attr("data-related") == "all") {
$item.css("display", "flex");
}
if ($li.attr("data-related") == $type) {
$item.css("display", "flex");
}
});
});
});
li {
padding: .9rem;
border: 1px solid;
cursor: pointer
}
.active {
background: blue
}
[data-section='table'] {
border: 2px solid;
pading: 1.3rem
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div data-section="table">
<ul class="ladder__ul">
<li data-related="all">All</li>
<li data-related="one" class="active">One</li>
<li data-related="two">two</li>
<li data-related="three">three</li>
</ul>
<div class="billing__list">
<div class="billing__item" data-type="one">One</div>
<div class="billing__item" data-type="two">Two</div>
<div class="billing__item" data-type="one">One</div>
<div class="billing__item" data-type="three">Three</div>
</div>
</div>
<div data-section="table">
<ul class="ladder__ul">
<li data-related="all">All</li>
<li data-related="a">a</li>
<li data-related="b" class="active">b</li>
</ul>
<div class="billing__list">
<div class="billing__item" data-type="a">a</div>
<div class="billing__item" data-type="b">b</div>
<div class="billing__item" data-type="a">a</div>
<div class="billing__item" data-type="b">b</div>
</div>
</div>

I think the problem is because $id is not equal to datarel
The .attr() method gets the attribute value for only the first element in the matched set.
ref: https://api.jquery.com/attr/
So your $id variable is always one because that's how you layout your elements in the html and One is the value of the first element with attribute data-type
<div class="billing__item" data-type="one">One</div>
<div class="billing__item" data-type="two">Two</div>
<div class="billing__item" data-type="one">One</div>
<div class="billing__item" data-type="three">Three</div>
I believe you are complicating things with the $id, you don't need it
$("[data-section='table']").each(function () {
var $container = $(this);
var item = $container.find(".billing__item");
var active = $container.find(".ladder__ul li.active");
var datarel = active.attr("data-related");
item.hide();
// Find out what Li has the active class, then return matching items on load:
if (datarel == "all") {
item.css("display", "flex");
}
$(".billing__item[data-type='" + datarel + "']").css("display", "flex");
});
Using datarel directly should suffice.

The problem from id. Because you get $id from item.attr("data-type") but the item is multiple element so it alway to get the first item data. That why at first section it work, but the second section work incorrect.
So you need to change your code like this:
//Retreive active list on load:::::::::::::::::::::::::::::::
$("[data-section='table']").each(function() {
var $container = $(this);
var item = $container.find(".billing__item");
var active = $container.find(".ladder__ul li.active");
var datarel = active.attr("data-related");
item.hide();
// Find out what Li has the active class, then return matching items on load:
if (datarel == "all") {
item.css("display", "flex");
} else {
$(".billing__item[data-type='" + datarel + "']", $container).css("display", "flex");
}
});

Related

How can I filter and mark words from an input without html markup being displayed?

I am trying to filter and mark word from a webpage, and Sajeeb Ahamed graciously assisted me with a piece of code that functions exactly as I wanted,however when I add and other element tags such as an list item or heading tag when I clear the input box it displays the HTML markup.
$(document).ready(function() {
$("#myInput").on("keyup", function() {
var value = $(this).val().toLowerCase();
$("#myDIV>*").map(function() {
var el = $(this);
var content = el.html().replace(/(<span class="highlighted">)|(<\/span>)/g, "");
el.html(content);
var hasText = el.text().toLowerCase().indexOf(value) > -1;
el.toggle(hasText);
if (hasText) {
// escape value for use in regex
var textRegex = new RegExp(value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
el.html(content.replace(textRegex, '<span class="highlighted">$&</span>'));
}
});
});
});
.highlighted {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="myInput" />
<!-- the new search -->
<div id="myDIV">
<p>This is a test</p>
<ul>
<li>This is a list item</li>
<li>This is a another list item</li>
</ul>
This is a link
</div>
This the code, it will only accept a paragraph tag. Does anybody have any idea why?
Thanks
It works with any element inside the element with id 'myDIV' at first level (not deep),because you are using this selector $("#myDIV>*"). Be sure that your tag is inside this rule.
UPDATE WITH NEW INFORMATION
$(document).ready(function() {
$("#myInput").on("keyup", function() {
var value = $(this).val().toLowerCase();
// Remove all class="highlighted" inside #myDIV
$("#myDIV").html($("#myDIV").html().replace(/(<span class="highlighted">)|(<\/span>)/g, ""))
$("#myDIV *").map(function() {
var el = $(this);
// Only in deep children aplly your logic
if (el.children().length == 0) {
var content = el.html().replace(/(<span class="highlighted">)|(<\/span>)/g, "");
el.html(content);
var hasText = el.text().toLowerCase().indexOf(value) > -1;
el.toggle(hasText);
if (hasText) {
// escape value for use in regex
var textRegex = new RegExp(value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
el.html(content.replace(textRegex, '<span class="highlighted">$&</span>'));
}
}
});
});
});
.highlighted {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="myInput" />
<!-- the new search -->
<div id="myDIV">
<p>This is a test</p>
<ul>
<li>This is a list item</li>
<li>This is a another list item</li>
</ul>
This is a link
</div>
You need to apply changes on deep children. and remove class highlight on begining
Most clean approach, rest div and start again. So before the start, I took the snapshot of div and save it. Every time data change I reconstruct it. Easy to understand and scale.
Suggestion: The UI should be data-driven. Not HTML/content-driven. You can create a list of data and construct on every change.
Checkout my second example
Do not mutate state/UI[React]
$(document).ready(function () {
const div = $("#myDIV").html();
$("#myInput").on("keyup", function () {
var value = $(this).val().toLowerCase();
$("#myDIV").html(div); //Reset
const p = $("#myDIV p");
var hasText = p.text().toLowerCase().indexOf(value) > -1;
hasText && highlight(p, value);
$("#myDIV li").map(function () {
var el = $(this);
var hasText = el.text().toLowerCase().indexOf(value) > -1;
if (hasText) {
highlight(el, value);
} else {
el.remove();
}
});
});
});
function highlight(elm, text) {
elm.html(
elm
.html()
.replace(new RegExp(`(${text})`), '<span class="highlighted">$1</span>')
);
}
.highlighted {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="myInput" />
<!-- the new search -->
<div id="myDIV">
<p>This is a test</p>
<ul>
<li>This is a list item</li>
<li>This is a another list item</li>
</ul>
This is a link
</div>
Using data-driven approach.
$(document).ready(function () {
const list = ["This is a list item", "This is a another list item"];
function buildUi(keyword) {
$("#filter .list").html("")
list.forEach((text) => {
if (!keyword || text.toLowerCase().indexOf(keyword) !== -1) {
text = text.replace(
new RegExp(`(${keyword})`),
'<span class="highlighted">$1</span>'
);
} else {
return;
}
const li = $(`<li>${text}</li>`);
$("#filter .list").append(li);
});
}
buildUi("");
$("#myInput").on("keyup", function () {
const keyword = $(this).val().toLowerCase()
buildUi(keyword)
});
});
.highlighted {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="myInput" />
<!-- the new search -->
<div id="filter">
<p>This is a test</p>
<ul class="list">
</ul>
This is a link
</div>
Hi guys so i found what i was looking for, thanks to the assistance of the great guys here and a lot of head banging, this script works for a local webpage search and filter, it must run in conjunction with jsquery mini and the hilitor.js file. This ought to be worth something to somebody out there. Happy coding guys.
<script src="../js/hilitor.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
window.addEventListener("DOMContentLoaded", function(e) {
var myHilitor2 = new Hilitor("playground");
myHilitor2.setMatchType("left");
document.getElementById("keywords").addEventListener("keyup", function(e) {
myHilitor2.apply(this.value);
}, false);
}, false);
$(document).ready(function(){
$("#keywords").on("keyup", function() {
var value = $(this).val().toLowerCase();
$("#playground *").filter(function() {
$(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
});
});
});
</script>
<label>Search And Filter</label><input id="keywords" type="text" placeholder="Search And Filter.." onKeyDown="if(event.keyCode === 32) return false;">
<div id="playground" >
<ul>
<li>Hey diddle diddle,</li>
<li>The cat and the fiddle,</li>
<li>The cow jumped over the moon.</li>
<li>The little dog laughed to see such sport,</li>
<li>And the dish ran away with the spoon.</li>
</ul>
</div>

Add text to unordered list on keyup event of textarea

Add text to unordered list on keyup event of textarea and there should't be any li when there is no text in textarea
HTML:
<textarea id="activityText"></textarea>
<div id="qwe">
<ul>
<li id="textFromTextArea"></li>
</ul>
</div>
jQuery:
$("#activityText").keyup(function () {
$("#textFromTextArea").text($("#activityText").val());
})
If there is no text in textarea then there should't be any list, can it be possible with jQuery
https://ibb.co/gjpDTL
Option 1:
I moved id from li to ul. I removed li element since you want to do not show marker.
<div id="qwe">
<ul id="textFromTextArea">
</ul>
</div>
<textarea id="ActivityText"></textarea>
and js:
$("#ActivityText").keyup(function () {
var val = $(this).val();
if($("#textFromTextArea li").length===0) {
var $li = $("<li>");
$li.html(val);
$("#textFromTextArea").append($li);
} else {
$("#textFromTextArea li").html(val);
}
});
Option 2:
Show/hide li element, preserve actual HTML you wrote but I add a CSS class hide
<div id="qwe">
<ul>
<li id="textFromTextArea" class="hide"></li>
</ul>
</div>
<textarea id="ActivityText"></textarea>
css
.hide {
display: none;
}
and js
$("#ActivityText").keyup(function () {
var val = $(this).val();
var $li = $("#textFromTextArea");
if(val.length === 0) {
$li.addClass("hide");
} else {
$li.removeClass("hide");
}
$li.html(val);
});
Try this
HTML:
<textarea id="activityText"></textarea>
<div id="qwe">
<ul id="list">
</ul>
</div>
jQuery:
var textval = '';
$("#activityText").on("keyup", function() {
textval = $(this).val();
if (textval.length > 0) {
$("#list").html('<li>' + textval + '</li>');
} else {
$("#list").empty();
}
});

Edit JavaScript function so it only applies to specified divs and not all divs

I have a function to carry out the action that I want, and it's working perfectly. I just don't know how to edit it so that it only applies to a certain class. Class would be better than IDs in this case, as some of the fields are dynamically generated and don't have specified IDs.
How can I edit this so that it only applies to a class name?
function spanClicked() {
var spanHtml = $(this).html();
var editableText = $("<textarea />");
editableText.val(spanHtml);
$(this).replaceWith(editableText);
editableText.focus();
// setup the blur event for this new textarea
editableText.blur(editableTextBlurred);
}
function editableTextBlurred() {
var html = $(this).val();
var viewableText = $("<span>");
viewableText.html(html);
$(this).replaceWith(viewableText);
// setup the click event for this new div
viewableText.click(spanClicked);
}
$(document).ready(function() {
$("span").click(spanClicked);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>
<span>Test</span>
</li>
<li>
<span>Test2</span>
</li>
<li>
<span>Test3</span>
</li>
</ul>
You can specify classes to select, instead of just using div:
function spanClicked() {
var spanHtml = $(this).html();
var editableText = $("<textarea />");
editableText.val(spanHtml);
$(this).replaceWith(editableText);
editableText.focus();
// setup the blur event for this new textarea
editableText.blur(editableTextBlurred);
}
function editableTextBlurred() {
var html = $(this).val();
var viewableText = $("<span>", {
class: 'editable'
});
viewableText.html(html);
$(this).replaceWith(viewableText);
// setup the click event for this new div
viewableText.click(spanClicked);
}
$(document).ready(function() {
$(".editable").click(spanClicked);
});
.editable {
color: red;
}
li {
margin: 20px;
font-family: arial;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>
<span>Test</span>
</li>
<li>
<span class="editable">Test2</span>
</li>
<li>
<span>Test3</span>
</li>
</ul>

remove class to sibling of elemnt jquery

I have this Jquery function, with a filter that adds a class named selected to the filter by click and shows all that data filter tags of the selected filter.
I want to define that if the sibling of the chosen element has a class named selected, that class needs to be removed from the rest and has to be added to only the selected element.
Function Script
(function ($) {
"use strict";
$.fn.filter = function (options) {
var defaults = {
nav: '[data-filter]' //
}
var $this = this,
settings = $.extend(defaults, options),
$target = $(settings.target),
selected = [];
return this.each( function() {
var $element = $(this);
$(settings.nav).each( function() {
$(this).click( function(event) {
// add selected class
$(this).toggleClass('selected');
// manipulate selected terms array
if ($.inArray($(this).data('filter'), selected) < 0 ) {
selected.push($(this).data('filter'));
} else {
var index = $.inArray($(this).data('filter'), selected);
selected.splice(index, 1);
}
// show/hide elements
$element.find('[data-filter-tags]').each( function() {
var terms = $(this).data('filter-tags').split(','),
show = null;
for (var i=0;i<selected.length;i++) {
show = ($.inArray(selected[i], terms) >= 0 && show !== false);
}
if (show || selected.length == 0) {
$(this).fadeIn();
} else {
$(this).fadeOut();
}
});
event.preventDefault();
});
});
});
};
}(jQuery));
HTML
<div id="tags">
<div id="cities" data-activeclass="selected">
תל אביב
רמת גן
הכל
<div>
</br>
משרה מלאה
משרה חלקית
הכל
</br>
מזכירות
הפעלה
הכל
</br>
</nav>
<div id="filter">
<div class="block" style="background: green" data-filter-
tags="time,kind,city,telaviv,full,sec">תל אביב משרה מלאה מזכירות</div>
<div class="block" style="background: blue" data-filter-
tags="time,kind,city,ramatgan,full,sec">רמת גן מלאה מזכירות</div>
<div class="block" style="background: blue" data-filter-
tags="time,kind,city,ramatgan,part,op">רמת גן חלקית הפעלה</div>
<div class="block" style="background: blue" data-filter-
tags="time,kind,city,telaviv,full,op">תל אביב מלאה הפעלה</div>
<div class="block" style="background: blue" data-filter-
tags="time,kind,city,ramatgan,part,sec">רמת גן חלקית מזכירות</div>
</div>
I want to define that if the sibling of the choosen elemnts has class named
"selected" remove the class from them and add it only to the selected element.
If clicking the element selects it and deselects all of its siblings, then in your click handler:
$(this).addClass("selected").siblings().removeClass("selected");
Simplified live example:
$("[data-filter]").on("click", function() {
$(this).addClass("selected").siblings().removeClass("selected");
});
[data-filter] {
border: 1px solid blue;
}
.selected {
background-color: yellow;
}
<div>
<span data-filter="1">one</span>
<span data-filter="2">two</span>
<span data-filter="3">three</span>
<span data-filter="4">four</span>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
If clicking the element toggles it, we want toggleClass (which you have) but the rest is the same:
$(this).toggleClass("selected").siblings().removeClass("selected");
...since the .siblings().removeClass("selected"); part just won't do anything if the current element was the one that was selected.
Simplified live example:
$("[data-filter]").on("click", function() {
$(this).toggleClass("selected").siblings().removeClass("selected");
});
[data-filter] {
border: 1px solid blue;
}
.selected {
background-color: yellow;
}
<div>
<span data-filter="1">one</span>
<span data-filter="2">two</span>
<span data-filter="3">three</span>
<span data-filter="4">four</span>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Compare string to each li element

I would like to compare a input against li elements but for some reason it's only checking the last li element. You can see this when running the script below everything with "test" works fine but not for "example" or "example 2"
$(document).ready(function() {
var $input = $('#autocomplete');
$(document).on('keyup', $input, function() {
var $val = $input.val().trim(),
$select = $('.autocomplete-content');
// Check if the input isn't empty
if ($val != '') {
$select.children('li').each(function() {
var $li = $(this),
$html = $li.html(),
$text = $li.text().trim();
// Set input value
$li.click(function() {
$input.val($text);
});
// Check if the value has at least 1 match else hide
if ($text.indexOf($val) !== -1) {
// Remove class hide when input changes and finds a match
if ($li.hasClass('hide')) {
$li.removeClass('hide');
}
// Unhide "ul.autocomplete-content"
if ($select.hasClass('hide')) {
$select.removeClass('hide'); // Show options
}
} else {
$li.addClass('hide');
$select.addClass('hide'); // Hide options
}
});
} else {
$select.addClass('hide');
}
});
});
.hide {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" id="autocomplete" class="autocomplete">
<ul class="autocomplete-content hide">
<li><img src="test.jpg"> example</li>
<li><img src="test.jpg"> example 2</li>
<li><img src="test.jpg"> test</li>
</ul>
So my question is how can I compare the input against each li element. Thanks in advance.
Update
Something I forgot to mention was that in the li element could be an img thats the reason why I used $li.text()
Instead of looping through all the li's when anything is entered in the input, you can try to use the pseudo selector :contains. This finds all elements with the text we have provided and if your intention is to find elements which starts with the search string, then you might need to improvise a little. This thread might be helpful.
$(document).ready(function() {
var $input = $('#autocomplete');
$(document).on('keyup', $input, function() {
var $val = $input.val().trim(),
$select = $('.autocomplete-content');
// Check if the input isn't empty
if ($val != '') {
$select.children('li').addClass('hide')
$select.children('li').filter(function() {
console.log($(this).text().indexOf($val))
return $(this).text().indexOf($val) !== -1;
}).removeClass('hide');
} else {
$select.children().addClass('hide');
}
});
});
.hide {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" id="autocomplete" class="autocomplete">
<ul class="autocomplete-content">
<li class="hide"><img src="test.jpg"> example</li>
<li class="hide"><img src="test.jpg"> example 2</li>
<li class="hide"><img src="test.jpg"> test</li>
</ul>
Need to remove $select.addClass('hide');. It's called when the input does not match test, even when it may match example.
$(document).ready(function() {
var $input = $('#autocomplete');
$(document).on('keyup', $input, function() {
var $val = $input.val().trim(),
$select = $('.autocomplete-content');
// Check if the input isn't empty
if ($val != '') {
$select.children('li').each(function() {
var $li = $(this),
$html = $li.html(),
$text = $li.text().trim();
// Set input value
$li.click(function() {
$input.val($text);
});
// Check if the value has at least 1 match else hide
if ($text.indexOf($val) !== -1) {
// Remove class hide when input changes and finds a match
if ($li.hasClass('hide')) {
$li.removeClass('hide');
}
// Unhide "ul.autocomplete-content"
if ($select.hasClass('hide')) {
$select.removeClass('hide'); // Show options
}
} else {
$li.addClass('hide');
//$select.addClass('hide'); // Hide options
}
});
} else {
$select.addClass('hide');
}
});
});
.hide {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" id="autocomplete" class="autocomplete">
<ul class="autocomplete-content hide">
<li>example</li>
<li>example 2</li>
<li>test</li>
</ul>
You have to set class hide to li instead of ul On keyup event of #autocomplete iterate through the lis, which one contains the textbox value remove class hide from that and add class hide which one not contains.
$(document).on('keyup', '#autocomplete', function () {
var text = $(this).val();
$('.autocomplete-content li').each(function () {
if ($(this).text().trim().indexOf(text) > -1) {
$(this).removeClass('hide');
} else {
$(this).addClass('hide');
}
});
});
.hide {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" id="autocomplete" class="autocomplete">
<ul class="autocomplete-content">
<li class="hide"><img src="test.jpg"> example</li>
<li class="hide"><img src="test.jpg"> example 2</li>
<li class="hide"><img src="test.jpg"> test</li>
</ul>

Categories