I'm trying to filter different posts kinda page. Each post has many different categories. But the search filter is two-layered, the main filter and then below a more specific selection of filters where I used checkboxes. The problem is that all categories are on the same level. How can I access each selected class based on the user filter input and then output the right post?
Categories and their classes are listed like this:
<span class="category">
<span class="cat Event">Event</span>
<span class="cat Developing">Developing</span>
<span class="cat SQL">SQL</span>
</span>
Where "Event" is in the main filter and the other two are in the second checkbox filter
The project is done in MVC .NET and for filtering functionality, I'm using jQuery
This is how to get each post in my view:
<div class="novice-list">
#foreach (var item in Model.Articles)
{
<div class="novica-container">
<a href="#DataContext.Current.RouteUrlManager.GetModuleLink("article", null, "details", item.Id, item.Title)">
<div class="media">
#{
string slika = string.Empty;
if (item.Id > 166 || item.Id == 159)
{
slika = $"{WellKnownStrings.StorageBaseUrl}{WellKnownStrings.ArticleImageContainer}/{item.FeaturedImage}";
}
else
{
slika = item.FeaturedImageUrl;
}
}
<div class="slika" style="background-image: url('#if (item.FeaturedImageUrl != "") { #slika }');">
</div>
<div class="media-body">
#*content*#
<div class="meta">
<span class="published">#item.DateFormated</span>
<span class="category">
#foreach (var cat in #item.Category)
{
<span class="cat #cat.Title">#cat.Title</span>
}
</span>
</div>
<h2>#item.Title</h2>
<p>#item.Summary</p>
</div>
</div>
</a>
</div>
}
</div>
this is how I get matches from first (Main) filtering:
isCheckedMain = true;
if (isCheckedMain) {
var selectedClass = $(this).attr('class');
// reset the active class on all the buttons
$('#filterOptions li').removeClass('show');
// update the active state on our clicked button
$(this).parent().addClass('show');
if (selectedClass == 'all') {
// show all our items
$('.novica-container').slideDown(1000, "linear");
}
else {
// hide all elements that don't share ourClass
$('.novica-container').hide();
// show all elements that do share ourClass
$('.novica-container').find('span.cat.' + selectedClass).parents('.novica-container').slideDown(1000, "linear");
}
}
And matches for checkbox filter:
$(".checkbox-filter :checkbox").click(function () {
isBoxChecked = true;
if (isCheckedMain && isBoxChecked) {
var selectedBox = $(this).attr('id');
var selectedMain = selectedClass;
if ($('input#' + selectedBox).is(':checked')) {
if ($('span.cat').hasClass(selectedMain)) {
$('.novica-container').hide();
$('span.cat.'+selectedMain).addClass(selectedBox);
$('.checkbox-filter :checkbox:checked').each(function () {
//Get the selected checkbox value
var selectedBox = $(this).attr('id');
$('.novica-container').find('span.cat.' + selectedMain ,'.'+selectedBox).parents('.novica-container').slideDown(700);
});
}
}
else if ($(this).is(':not(checked')) {
$('.novica-container').find('span.cat.' + selectedBox).parents('.novica-container').slideUp(700);
if (($('input[type="checkbox"]:checked').length === 0)) {
isBoxChecked = false;
$('.novica-container').slideDown(1000, "linear");
This is what I have so far, I'm trying to do something with true-false( if one is true, search only with the main filter, if both are true search more accurately with other categories. And I've been stuck here for days trying to merge both sides of the code. They both work separately but not together
Picture of filter
Found a solution for this.
I added all of the categories names one level higher in my View. So that class="category" now has values of all the other categories.
Example: class="category Event Developing SQL"
And then I could just use this jQuery to filter selected posts
$('span.category.' + selectedMain + '.' + selectedBox).parents('.novica-container').slideDown(700);
Related
I have a simple MVC view which the department name. There is a hierarchy on the department which I use in the nested Treeview node.
My current approach shows the TREEVIEW but only stops by 1 nested.
I want to display all the existing department hierarchy in the Treeview. Something like the figure below:
> Galaxy Department
|
|___> Moon Department
|
|____> Starts Department
|
|___> Cloud department
|
|___> And so on....
My mvc View:
<div class="col-md-3" style="border:1px solid black; height:725px; background-color:#FAFAFA">
<span style="font-weight:500;">Triple4</span>
#{
<div class="treeview">
#{
if (Model != null && Model.Count() > 0)
{
<ul>
#foreach (var i in Model)
{
<li>
<span class="collapse collapsible" data-loaded="false" pid="#i.DepartId"> </span>
<span>
#i.DepatName
</span>
</li>
}
</ul>
}
}
</div>
}
</div>
My Ajax :
$(".collapsible").on("click", function (e) {
e.preventDefault();
var this1 = $(this); // Get Click item
var data = {
pid: $(this).attr('pid')
};
var isLoaded = $(this1).attr('data-loaded'); // Check data already loaded or not
if (isLoaded == "false") {
$(this1).addClass("loadingP"); // Show loading panel
$(this1).removeClass("collapse");
// Now Load Data Part1
$.ajax({
url: "/Department/GetTreeViewList/",
type: "GET",
data: data,
dataType: "json",
success: function (d) {
$(this1).removeClass("loadingP");
if (d.length > 0) {
var $ul = $("<ul></ul>");
//var result;
$.each(d, function (i, ele) {
$ul.append(
$("<li></li>").append(
"<span class='collapse collapsible' data-loaded='false' pid='" + ele.DepartId + "'> </span>" +
"<span><a href='" + ele.NavUrl + "' id='directavail' >" + ele.DepatName + "</a></span>"
)
)
});
//$("[data-role=collapsible]").trigger("collapse");
$(this1).parent().append($ul);
$(this1).addClass('collapse');
$(this1).toggleClass('collapse expand');
$(this1).closest('li').children('ul').slideDown();
}
else {
// no sub menu
$(this1).css({ 'dispaly': 'inline-block', 'width': '15px' });
}
$(this1).attr('data-loaded', true);
},
error: function () {
alert("Error!");
}
});
}
else {
// if already data loaded
$(this1).toggleClass("collapse expand");
$(this1).closest('li').children('ul').slideToggle();
}
});
Any Subjection is very welcome.
I have found a very good approach from #Sourav Mondal
TreeView on Demand
Very quick and Accurate approach which I had to some changes on a JS script since it's old.
And the link it's just a tutorial and guideline.
Hopefully, it's helping someone too.
Sorry if title confuses. Hard to think of how to describe the problem in few words.
I have 3 different lists containing "level", "category", "time" for the game. Originally none of the lists' items have any class. When selected an item from each list, it gets a class 'activeSelection'.
I need to check if in each of the lists there is an item that has that class. If not, then show the warning under the list (there is a warning for each list) that at the moment is hidden. Also not let "startGame()" function to execute.
I need it to work so if 2 lists are missing that class, the warning would be shown under both of those lists. The same if all 3 lists are missing the class.
const levelList = document.querySelector('.levelList');
const categoryList = document.querySelector('.categoryList');
const timeList = document.querySelector('.timeList');
$(document).ready(function() {
$(playButton).on('click', function() {
if ($(levelList).children("li").hasClass('activeSelection') == false) {
$(levelList).find('.warning').show();
} else {
startGame();
} /** that's how I check one of the lists. I need to check all 3
and if at least one does not have a class give a warning, but not
stop checking until all 3 lists are checked to give the rest of
the warnings if needed **/
});
});
.warning {
display: none;
}
<div class="list">
<div class="levelList">
<h2>level</h2>
<ul>
<li>easy</li>
<li>medium</li>
<li>hard</li>
</ul>
<p class="warning">! please select level !</p>
</div>
<div class="categoryList">
<h2> category</h2>
<ul>
<li>movies</li>
<li>songs</li>
<li>people</li>
<li>animals</li>
<li>random things</li>
</ul>
<p class="warning">! please select Category !</p>
</div>
<div class="timeList">
<h2>time</h2>
<ul>
<li>1 min</li>
<li>2 min</li>
<li>3 min</li>
</ul>
<p class="warning">! please select time !</p>
</div>
</div>
You can try doing all 3 checks separately:
$(document).ready(function() {
$(playButton).on('click', function() {
var status = 0;
if ($(levelList).children("li").hasClass('activeSelection') == false) {
$(levelList).find('.warning').show();
} else {
status += 1;
}
if ($(categoryList).children("li").hasClass('activeSelection') == false) {
$(categoryList).find('.warning').show();
} else {
status += 1;
}
if ($(timeList).children("li").hasClass('activeSelection') == false) {
$(timeList).find('.warning').show();
} else {
status += 1;
}
if (status === 3)
startGame();
});
I'm attempting to extract the ID of checkbox when it is selected, but I can't seem to find a way that fits what I'm trying to do.
First I have the HTML / Angular for the check boxes. The check boxes are generated by three tiers. First there's a service level, then the day of the week and then the service itself (which are what the check boxes are). The service level makes an accordion, the days of the week are loaded into tabs and the check boxes themselves come in as normal.
<div class="delivery-rules">
<div class="panel-group" id="accordion">
<div class="panel panel-default" ng-repeat="level in settings.serviceLevels">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#{{level.LevelTmsCode}}">{{level.LevelName}}</a>
</h4>
</div>
<div id="{{level.LevelTmsCode}}" class="panel-collapse collapse in">
<div class="panel-body">
<ul class="nav nav-tabs">
<li id="{{day.Day}}-{{level.LevelTmsCode}}-tab" ng-repeat="day in settings.serviceDays">
<a id="{{day.Day}}-{{level.LevelTmsCode}}" href="#tabContent-{{day.Day}}-{{level.LevelTmsCode}}" ng-click="settings.changeTab(day, level, $event)">{{day.Day}}</a>
</li>
</ul>
<div class="tabContent" id="tabContent-{{day.Day}}-{{level.LevelTmsCode}}" ng-repeat="day in settings.serviceDays">
<h4>{{day.Day}}</h4>
<div class="time-check" ng-repeat="service in settings.services">
<input type="checkbox" value="None" ng-change="settings.showChecked(settings.rules, $event)" ng-model="settings.selected[$index]" class="time-check-input" id="{{level.LevelTmsCode}}-{{day.Day}}-{{service.TimeValidation}}" name="check"/>
<label for="{{level.LevelTmsCode}}-{{day.Day}}-{{service.TimeValidation}}" class="time-check-input"></label> <span>{{service.TimeValidation}}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
The arrays that build those check boxes, tabs and accordion are loaded with data from a standard http post request. Then once that is complete I place all the possible combinations of all three arrays into one big array and set their checked attribute to false.
// Get Service Levels to Build Delivery Rules Accordion
settings.getDeliveryServices = function() {
$http.get(resourceBase + "api/service/levels").success(function(data) {
settings.serviceLevels = data;
// Get Service Days
$http.get(resourceBase + "api/service/days").success(function(days) {
settings.serviceDays = days;
// Build the Accordion
setTimeout(() => settings.triggerClick(settings.serviceLevels), 500);
$http.get(resourceBase + "api/service/services").success(function (services) {
settings.services = services;
// Build a collection of all possible rules
for (var a = 0; a < settings.serviceLevels.length; a++) {
settings.rulesTmsCode.push(settings.serviceLevels[a].LevelTmsCode + "-");
}
for (var b = 0; b < settings.serviceDays.length; b++) {
settings.rulesDay.push(settings.serviceDays[b].Day + "-");
}
for (var c = 0; c < settings.services.length; c++) {
settings.rulesTime.push(settings.services[c].TimeValidation);
}
var allArrays = [settings.rulesTmsCode, settings.rulesDay, settings.rulesTime];
function allPossibleCases(arr) {
if (arr.length === 1) {
return arr[0];
} else {
var result = [];
var allCasesOfRest = allPossibleCases(arr.slice(1));
for (var i = 0; i < allCasesOfRest.length; i++) {
for (var j = 0; j < arr[0].length; j++) {
result.push(arr[0][j] + allCasesOfRest[i]);
}
}
return result;
}
}
var uncheckedRules = allPossibleCases(allArrays);
for (var i = 0; i < uncheckedRules.length; i++) {
settings.rules.push({
id: uncheckedRules[i],
checked: false
});
}
});
});
});
}
When each box is checked I'm trying to manipulate the combination array so that the selected combination is set to true.
// Check and Filter Rules to send
settings.showChecked = function (object, $event) {
for (var i = 0; i < settings.rules.length; i++) {
if (settings.rules.hasOwnProperty(i)) {
if (typeof settings.rules[i].id == settings.selected[i]) {
settings.showChecked(settings.rules[i], settings.selected[i]);
}
if (settings.rules[i].id === settings.selected[i]) {
settings.rules[i].checked = true;
}
}
}
console.clear();
console.log(settings.rules);
}
Currently, nothing is set to true as I can't seem to be able to get the ID from the checkbox to compare it with the string stored in the ID value of the combination array. So basically I need the ID of the checkbox that was selected and I need to pass that through to the ng-change event.
Try Some thing like this..
<input id={{emp.name}} type=checkbox value="{{emp.name}}" ng-change="settings.showChecked(settings.rules, $event)>
settings.showChecked=function(object,$event)
{
var el = event.target.id
}
the other way is you can pass id value in place event like below
<select id="hairColorComponent" ng-model="hairColor"
ng-options="option.name for option in hairColorData"
ng-change="updateUserData('hairColorComponent')">
$scope.updateUserData = function (id) {
var element = jQuery('#'+id);
};
I have a script that produces an array of forms, with each form affecting the available options for the next form. The awesome martin booth solved the problem of getting the displayed values to update as new forms are added.
however, I have an observable array (defaultSampleRates) that sits outside the forms array, and for the life of me I can't get the form to push items into that array. I've tried declaring it in a dozen different places in a dozen different ways, but it just won't stick.
basically I need the 'Default sample rate' drop-down to show the sample rates that have been selected in the form above (the user must only be able to choose a default sample rate from a displayed one, rather than from the full list).
Any tips much helpo brain pain. fiddle here: http://jsfiddle.net/3lliot/9vsa4hh7/
html:
<body>
<div style="float:left; width:60%">
<div data-bind="foreach: forms">
<div style="float:left; margin-right:20px"> <span>
<!-- This is a *view* - HTML markup that defines the appearance of your UI -->
<p><span style="color:#AB0002">Sample rate element <span data-bind="text: formNum"></span></span>
</p>
<p>Sample rate (Hz):
<select data-bind="options: sampleRates, value: selectedSampleRate"></select>
</p>
</span>
</div>
</div>
<div style="float:left; clear:both; margin-bottom:20px">
<hr/>
<button data-bind="click: addForm">Add <srate> element</button>
<button data-bind="click: removeForm">Remove</button>
<p>Default sample rate:
<select data-bind="options: defaultSampleRates, value: selectedDefaultSampleRate"></select>
</p>
</div>
</div>
<div style="float:right; width:38%; overflow:scroll; border-left:thin; border-left-style:solid; border-left-color:#dfdfdf;padding-left: 1%"> <span class="code"><audio></span>
<ul data-bind="foreach: forms">
<li>
<!-- render the json --> <span class="code"> <srate id="<span data-bind="text: formNum"></span>">
<br/> <sample_rate><span data-bind="text: selectedSampleRate"></span></sample_rate>
<br/> </srate></span>
</li>
</ul> <span class="code"> <default_srate><span data-bind="text: selectedDefaultSampleRate"></span></default_srate></span>
<br/><span class="code"></audio></span>
</div>
</body>
js:
// This is a simple *viewmodel* - JavaScript that defines the data and behavior of your UI
//window.onload = startKnockout;
window.onload = startKnockout;
var formNum;
var i = -1;
var selectedSampleRates = [];
function Form(allSampleRates, forms) {
var self = this;
// Declare observables
self.selectedSampleRate = ko.observable();
self.formNum = ko.observable();
self.sampleRates = ko.computed(function () {
var formsValue = forms(),
availableSampleRates = ko.utils.arrayFilter(allSampleRates, function (sampleRate) {
return !ko.utils.arrayFirst(formsValue, function (form) {
if (form != self) {
if (form.selectedSampleRate() === sampleRate) {
if (selectedSampleRates.indexOf(sampleRate) === -1) {
selectedSampleRates.push(sampleRate);
}
}
return form.selectedSampleRate() === sampleRate;
} else {
return form != self;
}
});
});
return availableSampleRates;
});
// count how many srate elements there are
i++;
self.formNum = i;
}
var Vm = function () {
var self = this;
var item = 0,
allSampleRates = ['192000', '176400', '96000', '88200', '48000', '44100'];
// declare observables for options outside the srate elements
self.selectedDefaultSampleRate = ko.observable();
// add remove forms stuff
self.forms = ko.observableArray([]);
self.forms.push(new Form(allSampleRates, self.forms));
item++;
self.addForm = function () {
if (i < 5) {
self.forms.push(new Form(allSampleRates, self.forms));
item++;
} else {
alert("Can't have more than 6 <srate> elements!")
}
};
self.removeForm = function () {
if (item > 1) {
self.forms.splice(item - 1, 1);
item--;
i--;
} else {
alert("Must have at least one <srate> element!")
}
};
// define arrays for options outside srate elements
self.defaultSampleRates = ko.observableArray([]);
return self;
}
// Activates knockout.js
function startKnockout() {
ko.applyBindings(new Vm());
};
You can make use of selectedOptions binding to add defaultSample rate.
I changed select sampleRates code to this
<select data-bind="options: sampleRates, value: selectedSampleRate, selectedOptions: $root.defaultSampleRates"></select>
Notice selectedOptions binding there..
Should work as per your need.
Updated Fiddle Demo here : http://jsfiddle.net/rahulrulez/9vsa4hh7/3/
I hope that's what you wanted.
I would like to search by any term (name, user, from, price), and display the div into top and hide the ones who doesn't have the typed value.
Here's the jsfiddle: http://jsfiddle.net/Sc9ys/10/
I would like to have the same result as the jquery mobile table filter http://demos.jquerymobile.com/1.4.0/filterable/
Where you can search for any term.
I know that for search for any term I should use $(list).find("li *:)... but I can't figure out how to display the items properly. If you test my jsfiddle it doesn't work very well.
Edit: As asked by the user below, here's some more info.
<ul id='list'>
<li>
<div class='row'>
<div class='middle'>
<ul>
<li><h3>Stackoverflow</h3></li>
<li><span>User</span></li>
<li><span>London</span></li>
</ul>
</div>
<div style='clear: both'></div>
</div>
</li>
</ul>
$("#search").change( function () {
$(list).find("li *:not(:Contains(" + filter + "))").parent().hide();
});
DEMO
The idea is in
$("#ul_container").find("li").filter(function () {//your comparing logic here });
Here, try this out. Honesty I couldn't read thru your code, so I made this example. I added the sub items (spans that contain data to be searched) in an array datalist by their class name.
Generic Search Function.
HTML
<input type="text" id="search" />
<ul id="ul_container">
<li class="listItem">
<span class="car">Honda</span>
<span class="country">Japan</span>
</li>
<li class="listItem">
<span class="car">BMW</span>
<span class="country">Germany</span>
</li>
</ul>
Script:
//Capture user input
$("#search").on("keyup change", function () {
var str = $.trim($(this).val());
if (str) {
search(str);
} else {
// if no input, then show all
$(".listItem").show();
}
});
//the search part.
var datalist = ["car", "country"];
function search(toFind) {
//select all li and loop thru them one by one
$("#ul_container").find("li").filter(function () {
var $li = $(this);//hold current li in a variable
//loop thru all sub spans by their class and check if the toFind keyword is there
// you modify this step, i use it to specify which sub span to be searched. Sometimes I don't want all field to be searched, only the ones I select.
for (var i = 0; i < datalist.length; i++) {
//hold the span in a var called $item
var $item = $li.children("." + datalist[i]);
var content_str = $item.html();//get the actual string
//the comparing code
if (content_str.toLowerCase().indexOf(toFind.toLowerCase()) >= 0) {
$li.show();
break;
} else {
$li.hide();
}
}
});
}
Solved guys. Thank you all.
You can see the following example working at: http://jsfiddle.net/Sc9ys/29/
$('#search').on('keyup change', function(){
var str = $.trim($(this).val());
if (str) {
search(str, $("#list"));
} else {
$("#list").find('li').show();
/* The <li> are display: none, to show them again if the input type is clear,
we must find those <li> and show them. Showing only the #list isn't enough. */
}
});
function search(toFind, list){
$(list).find('li').filter(function() {
$li = $(this);
$li.find(".middle :contains(" + toFind +")").parent().parent().slideDown();
$li.find(".middle").not(":contains(" + toFind + ")").parent().parent().slideUp();
});
}
/* Function to search with the input lowercase */
$.expr[":"].contains = $.expr.createPseudo(function(arg) {
return function( elem ) {
return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
};
});
Edit: Made some adjustments according to the help of user #Joraid.