Dynamically Filtering SharePoint List Views using Javascript


The List View Web Part has great capability for filtering displayed data using the web parts header controls..

List View Web Part Filtering

If you’re building out a UI which incorporates a list view web part, you can also perform the filtering using Javascript in response to your UI events, hooking into the LVWP filtering code.

As an example, what I want to build is a UI which allows me to filter a LVWP using Year and Month controls shown below..

flvwp-01

So when I click on a Year, the LVWP is filtered by that year..

flvwp-02

And likewise for the Month..you get the picture

flvwp-03

In the screenshots above, the columns I’m filtering by are present in the view, but they don’t have to be I’ve only shown them there for demonstration purposes.

This can be done in both 2010 and 2013, though the code is different; in 2013 we call the RefreshPaging function and in 2010 we call the page postback function __doPostBack using the control id of the LVWP with correctly formed event arguments – if you’ve ever done any work with the SPGridView control this will sound familiar.

INPLVIEW.JS

Both versions make use of the javascript ContextInfo ‘objects’ which SharePoint whips up for list view web parts and in particular relies on the InitAllClvps function found in this file.

In my implementation I’m using Knockout to manage the filter UI and subscriptions to the Knockout Year/Month observables to perform the actual filtering.

HTML (2013)

<link rel="stylesheet" type="text/css" href="../SiteAssets/sgm-meetings/css/fontello.css" runat="server" />
<!--[if IE 7]>
<link rel="stylesheet" type="text/css" href="../SiteAssets/sgm-meetings/css/fontello-ie7.css" runat="server" />
<![endif]-->
<!-- App CSS -->
<link rel="stylesheet" type="text/css" href="../SiteAssets/sgm-meetings/css/cdtm-main.css" runat="server" />
<!-- Lib JS -->
<script type="text/javascript" src="../SiteAssets/sgm-meetings/scripts/knockout-3.1.0.js"></script>
<!-- App JS -->
<script type="text/javascript" src="../SiteAssets/sgm-meetings/app/cdtm-app.js"></script>
<div id="cdtm" class="cdtm">
<div class="cdtm-wrapper">
<div class='box-5'>
<div class='header'>
<h1>
<span data-bind='text: LibraryTitle'></span><span data-bind='visible: LibraryTitle &amp;&amp; LibrarySubTitle' style='display:none;'> <i class='icon-angle-right' style=''></i> </span><span data-bind='text: LibrarySubTitle'></span>
<img data-bind='visible: Working' src='/_layouts/15/images/hig_progcircle_loading24.gif' border='0' />
</h1>
</div>
<div class="nav">
<div class="row">
<div class='left'>
<ul class='years' data-bind='foreach: FilterYears' title='filter the meetings by year'>
<li data-bind='css: { selected: $parent.isSelectedYear($data) }'>
<a href='#'
data-bind='
text: $data,
click: $parent.filterByYear'>
</a></li>
</ul>
<ul class='months' data-bind='foreach: FilterMonths' title='filter the meetings by month'>
<li data-bind='css: { selected: $parent.isSelectedMonth($data) }'>
<a href='#'
data-bind='
text: $data,
click: $parent.filterByMonth'>
</a></li>
</ul>
</div>
<div class='right'>
<div class='search-wrapper'>
<input class='searchfield' type='text' value='' data-bind='value: FilterTextIm, valueUpdate: "afterkeydown"' title='filter the meetings by name' />
<i class='icon-search'></i>
</div>
</div>
</div>
</div>
<div class="maincontent">
Content Goes Here
</div>
</div>
</div>
</div>

JAVASCRIPT (2013)

(function(module,$) {
"use strict";
window.pd = window.pd || {};
pd.FilterLibraryViewModel = function() {
/* observable state */
this.FilterTextIm = ko.observable(''),
this.FilterText = ko.computed(this.FilterTextIm)
.extend({ throttle: 400 }),
this.LibraryTitle = ko.observable(null),
this.LibrarySubTitle = ko.observable(),
this.FilterYears = new ko.observableArray([]),
this.FilterMonths = new ko.observableArray(['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']),
this.SelectedYear = ko.observable(''),
this.SelectedMonth = ko.observable('');
this.Working = ko.observable(true);
/* populate years filter as Current Year..-=4 */
for(var cy = parseInt(new Date().getFullYear()), y = cy; y > (cy-4); y--) { this.FilterYears.push(y.toString()); }
/* model actions */
this.filterByYear = function(year) {
this.FilterTextIm(''); // zap the filter text
year = (year === this.SelectedYear()) ? '' : year;
this.SelectedYear(year);
}.bind(this);
this.filterByMonth = function(month) {
this.FilterTextIm(''); // zap the filter text
month = (month === this.SelectedMonth()) ? '' : month;
this.SelectedMonth(month);
}.bind(this);
/* model behaviour */
this.isSelectedYear = function (year) {
var y = this.SelectedYear();
return (y == year);
}.bind(this);
this.isSelectedMonth = function (month) {
var m = this.SelectedMonth();
return (m == month);
}.bind(this);
}
pd.FilterLibraryApp = function() {
var
viewModel = new pd.FilterLibraryViewModel(),
debug = true,
wpCtx = null,
wpEvent = null,
_module = {
start: start
};
return _module;
function locateListView() {
EnsureScript("inplview", typeof InitAllClvps, null, true);
InitAllClvps();
for(var k in g_ctxDict) {
if (debug && window.console) console.log(g_ctxDict[k]);
if (g_ctxDict[k].ListTitle === 'Documents') {
wpCtx = window['ctx'+g_ctxDict[k].ctxId];
break;
}
}
if (debug && window.console) {
console.log(wpCtx.clvp);
console.log(wpCtx.clvp.ctx);
console.log(wpCtx.clvp.ctx.view);
console.log(wpCtx.clvp.ctx.ListSchema.Filter);
console.log(wpCtx.clvp.ctx.ListData.FilterLink);
}
var clvp = (wpCtx && wpCtx.clvp) ? wpCtx.clvp : null;
wpEvent = { clvp: clvp, event: { currentCtx: { clvp: clvp } } };
viewModel.LibraryTitle(wpCtx.ListTitle);
viewModel.LibrarySubTitle('All');
viewModel.Working(false);
//$('#' + wpCtx.wpq + '_ListTitleViewSelectorMenu_Container').hide();
}
function filterByText() {
if (!wpCtx) {
alert('Unable to filter: the list view webpart could not be located!');
return;
}
var filterText = this.FilterText();
if (debug && window.console) { console.log(">>FilterByText: ["+filterText+"]"); }
var $webpart = $("#WebPart" + wpCtx.wpq);
$webpart.find("tr.ms-itmhover").each(function() {
var $tr = $(this);
if (filterText && filterText.length) {
var
r = new RegExp(filterText,'gi'),
matched = false,
$tds = $tr.find(".ms-vb-title,.ms-vb-2").first();
$tr.find(".ms-vb-title,.ms-vb2").each(function() {
var td = $(this).text();
if(!r.test(td)) return true;
matched = true;
return false;
});
if(matched) $tr.fadeIn(250);
else $tr.fadeOut(250);
} else {
$tr.fadeIn(250);
}
});
}
function onFilterChange(filterType) {
if (!wpCtx) {
alert('Unable to filter: the list view webpart could not be located!');
return;
}
var
filterColumns = [],
filterValues = [],
fyv = this.SelectedYear(),
fym = this.SelectedMonth();
if (fyv && fyv.length) {
filterColumns.push('catYear');
filterValues.push(fyv);
}
if (fym && fym.length) {
filterColumns.push('catMonth');
filterValues.push(fym);
}
var filterUrl;
if (!filterColumns.length) {
/* clear filter */
filterUrl = _spPageContextInfo.webAbsoluteUrl + window.location.pathname + '?View=' + wpCtx.clvp.ctx.view + '&FilterClear=1';
} else {
filterUrl = _spPageContextInfo.webAbsoluteUrl + window.location.pathname + '?View=' + wpCtx.clvp.ctx.view;
for(var i = 0; i < filterColumns.length; i++) {
if (debug && window.console) { console.log(">>FilterBy: ["+filterColumns[i]+"] = ["+filterValues[i]+"]"); }
filterUrl += '&FilterField'
+ (i+1) + '=' + filterColumns[i]
+ '&FilterValue'
+ (i+1) + '=' + filterValues[i];
}
if (debug && window.console) { console.log(">>FilterUrl: "+filterUrl); }
}
if (wpEvent.clvp == null || wpEvent.clvp.ctx == null || !wpEvent.clvp.ctx.IsClientRendering) {
HandleFilter(wpEvent.event, filterUrl);
} else {
wpEvent.clvp.RefreshPaging(filterUrl);
wpEvent.clvp.ctx.queryString = filterUrl;
}
}
function start() {
ko.applyBindings(viewModel, $('#cdtm').get(0));
viewModel.FilterText.subscribe(filterByText, viewModel, "change");
viewModel.SelectedYear.subscribe(onFilterChange.bind(viewModel, 'year'));
viewModel.SelectedMonth.subscribe(onFilterChange.bind(viewModel, 'month'));
locateListView();
}
}();
$(pd.FilterLibraryApp.start);
})({ Name: 'Module' },jQuery);

Looking into the implementation the code in pd.FilterLibraryViewModel is just the knockout viewmodel, the code in pd.FilterLibraryApp is whats sets things up and performs the filtering.

The locateListView function ensures that the inplview.js file is loaded (via SOD) and calls InitAllClvps(). It then looks through the list of ContextInfo objects looking for the one for our list view web part. It then performs some object mangling to get things right for the filtering to work – you’ll have to delve deep into inplview.js to see why, but this is just what SharePoint requires.

Having got everything setup, when clicking on a Year or Month the onFilterChange function is called in response to a change in the viewmodel observables, this works out the filter url required based on the viewmodel observable state and calls the RefreshPaging (inplview.js) function – the list view web part then gets filtered in place without requiring a post back.

Views with Person/Group Columns

For some reason I’ve yet to fathom, when you include an Person/Group column in the view, in-place filtering does not work and a full page postback occurs.

Although not the subject of this post, the sample code included here also includes a text box which filters the table rows of the list view web part, but only the rows currently displayed.

How to Use.

Simply drop a list view web part on to a page and configure the view as you’d like it, then drop a content editor web part onto the page and configure it to point to the HTML file – which includes the script and CSS files.

Links to Resources

CSS (2013)

/* CLEARFIX */
.cdtm-wrapper .row:before,
.cdtm-wrapper .row:after {
content: "";
display: table;
}
.cdtm-wrapper .row:after {
clear: both;
}
.cdtm-wrapper .row {
zoom: 1; /* For IE 6/7 (trigger hasLayout) */
}
.cdtm-wrapper .row .left {
float: left;
}
.cdtm-wrapper .row .right {
float: left;
}
/* LAYOUT */
.cdtm {
display: inline-block;
width: 100%;
/*border: 1px dotted #aaa;*/
}
.cdtm-wrapper {
font-family: 'Open Sans',Calibri,Verdana,Arial,'Sans Serif';
width: 100%;
font-size: 12px;
}
/* PADDING/MARGINS */
.cdtm-wrapper > .box-5 {
padding: 5px 10px 10px 0;
}
.cdtm-wrapper .header {
margin: 0;
padding: 0;
}
.cdtm-wrapper .nav {
margin: 0;
padding: 0;
}
.cdtm-wrapper article {
margin: 0;
padding: 0;
}
/* HEADER & NAVIGATION */
.cdtm-wrapper .header h1, .cdtm-wrapper .header h2, .cdtm-wrapper .header h3, .cdtm-wrapper .header h4, .cdtm-wrapper .header h5 {
color: #415A6C;
margin: 0 0 8px 0;
padding: 0;
font-size: 18pt;
line-height: 18pt;
font-weight: 700;
}
.cdtm-wrapper .header h2 {
font-size: 16pt;
}
.cdtm-wrapper .header h3 {
font-size: 14pt;
}
.cdtm-wrapper .header h4 {
font-size: 12pt;
}
.cdtm-wrapper .header h5 {
font-size: 12pt;
}
.cdtm-wrapper .header h1 *[class^='icon-'] {
font-size: 20px;
line-height: 20px;
}
.cdtm-wrapper .nav .left {
display: inline-block;
width: 60%;
min-width: 290px;
max-width: 410px;
}
.cdtm-wrapper .nav .right {
display: inline-block;
width: 40%;
max-width: 250px;
}
.cdtm-wrapper .nav ul {
list-style: none outside none;
margin: 0 0 5px;
padding: 0;
}
.cdtm-wrapper .nav ul.years {
font-size: 1.5em;
}
.cdtm-wrapper .nav ul.months {
font-size: 1.2em;
}
.cdtm-wrapper .nav li {
display: inline-block;
margin: 0;
padding: 0;
}
.cdtm-wrapper .nav li a, .cdtm-wrapper .nav li a:link {
color: #E24912;
display: inline-block;
padding: 2px 3px;
margin-right: 2px;
}
.cdtm-wrapper .nav li a:hover {
color: #002596;
cursor: pointer;
background-color: rgb(0,180,240);
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; /* IE 8/9 */
filter: alpha(opacity=50); /* IE 5.5 - 7 */
background-color: rgba(0,180,240,0.50);
}
.cdtm-wrapper .nav li.selected a,
.cdtm-wrapper .nav li.selected a:link {
background-color: rgb(0,180,240);
background-color: rgba(0,180,240,1);
color: whitesmoke;
}
/* SEARCH FIELD */
.cdtm-wrapper .nav .right .search-wrapper {
border: 1px solid #c8c8c8;
border-radius: 5px;
padding: 5px;
position: relative;
}
.cdtm-wrapper .nav .right .search-wrapper .searchfield {
background-color: #fff;
border: medium none;
border-radius: 3px;
color: #aeaeae;
display: inline-block;
font-size: 1.5em;
font-weight: normal;
margin: 0;
transition: all 0.2s linear 0s;
width: 88%;
}
.cdtm-wrapper .nav .right .search-wrapper .searchfield:focus {
color: #858585;
}
.cdtm-wrapper .nav .right .search-wrapper .icon-search {
color: #d5d8de;
display: inline-block;
font-size: 2em;
line-height: 10px;
position: absolute;
right: 6px;
text-indent: -3px;
width: 26px;
}
.cdtm-wrapper .nav .right .search-wrapper .icon-search:hover {
color: #415A6C;
cursor: pointer;
}
/* MAIN CONTENT */
.cdtm-wrapper .maincontent {
display: none;
margin-top: 10px;
border-top: 1px solid #DDE0E4;
padding: 10px 2px 0 2px;
}
.ms-webpartzone-cell {
margin: auto auto 0;
}
/* fixup SP display */
#MSOZoneCell_WebPartWPQ2 .ms-PartSpacingVertical {
margin-top: 0;
}
div.welcome {
padding-top: 0;
}
[class]#pageStatusBar {
margin: 10px 0 -10px 0;
}

HTML (2010)

<link rel="stylesheet" type="text/css" href="../SiteAssets/sgm-meetings/css/fontello.css" runat="server" />
<!--[if IE 7]>
<link rel="stylesheet" type="text/css" href="../SiteAssets/sgm-meetings/css/fontello-ie7.css" runat="server" />
<![endif]-->
<!-- Lib CSS -->
<link rel="stylesheet" type="text/css" href="../SiteAssets/sgm-meetings/css/cdtm-main.css" runat="server" />
<!-- Lib JS -->
<script type="text/javascript" src="../SiteAssets/sgm-meetings/scripts/knockout-3.1.0.js"></script>
<!-- App JS -->
<script type="text/javascript" src="../SiteAssets/sgm-meetings/app/cdtm-app.js"></script>
<div id="cdtm" class="cdtm">
<div class="cdtm-wrapper">
<div class='box-5'>
<div class='header'>
<h1>
<span data-bind='text: LibraryTitle'></span><span data-bind='visible: LibraryTitle &amp;&amp; LibrarySubTitle' style='display:none;'> <i class='icon-angle-right' style=''></i> </span><span data-bind='text: LibrarySubTitle'></span>
<img data-bind='visible: Working' src='/_layouts/images/hig_progcircle_loading24.gif' border='0' />
</h1>
</div>
<div class="nav">
<div class="row">
<div class='left'>
<ul class='years' data-bind='foreach: FilterYears' title='filter the meetings by year'>
<li data-bind='css: { selected: $parent.isSelectedYear($data) }'>
<a href='#'
data-bind='
text: $data,
click: $parent.filterByYear'>
</a></li>
</ul>
<ul class='months' data-bind='foreach: FilterMonths' title='filter the meetings by month'>
<li data-bind='css: { selected: $parent.isSelectedMonth($data) }'>
<a href='#'
data-bind='
text: $data,
click: $parent.filterByMonth'>
</a></li>
</ul>
</div>
<div class='right'>
<div class='search-wrapper'>
<input class='searchfield' type='text' value='' data-bind='value: FilterTextIm, valueUpdate: "afterkeydown"' title='filter the meetings by name' />
<i class='icon-search'></i>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

JAVASCRIPT (2010)

(function(module,$) {
"use strict";
window.pd = window.pd || {};
pd.MeetingsViewModel = function() {
/* observable state */
this.FilterTextIm = ko.observable('');
this.FilterText = ko.computed(this.FilterTextIm)
.extend({ throttle: 400 });
this.LibraryTitle = ko.observable('');
this.LibrarySubTitle = ko.observable('');
this.FilterYears = new ko.observableArray([]);
this.FilterMonths = new ko.observableArray(['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']);
this.SelectedYear = ko.observable('');
this.SelectedMonth = ko.observable('');
this.Working = ko.observable(true);
/* populate years filter as Current Year..-=4 */
for(var cy = parseInt(new Date().getFullYear()), y = cy; y > (cy-4); y--) { this.FilterYears.push(y.toString()); }
/* model actions */
this.filterByYear = function(year) {
this.FilterTextIm(''); // zap the filter text
year = (year === this.SelectedYear()) ? '' : year;
this.SelectedYear(year);
}.bind(this);
this.filterByMonth = function(month) {
this.FilterTextIm(''); // zap the filter text
month = (month === this.SelectedMonth()) ? '' : month;
this.SelectedMonth(month);
}.bind(this);
/* model behaviour */
this.isSelectedYear = function (year) {
var y = this.SelectedYear();
return (y == year);
}.bind(this);
this.isSelectedMonth = function (month) {
var m = this.SelectedMonth();
return (m == month);
}.bind(this);
}
pd.FilterLibraryApp = function() {
var
viewModel = new pd.MeetingsViewModel(),
debug = true,
wpCtx = null,
wpEvent = null,
filterControlId = null,
listViewTableId = null,
_module = {
start: start
};
return _module;
function locateListView() {
var fn = function() {
InitAllClvps();
// find the CTX for the LVWP
for(var k in g_ctxDict) {
if (debug && window.console) console.log(g_ctxDict[k]);
if (g_ctxDict[k].ListTitle === module.LibraryTitle) {
wpCtx = window['ctx'+g_ctxDict[k].ctxId];
break;
}
}
if (debug && window.console) {
console.log(wpCtx.clvp);
console.log(wpCtx.clvp.ctx);
console.log(wpCtx.clvp.ctx.view);
}
/* this isn't used in the 2010 version, not figured out if/how to use it yet
var clvp = (wpCtx && wpCtx.clvp) ? wpCtx.clvp : null;
wpEvent = { clvp: clvp, event: { currentCtx: { clvp: clvp } } };
*/
// find the LVWP and table containing the rows
var
$wpnode = $('#'+wpCtx.clvp.wpq).first(),
$wpTableNode = $wpnode.find("table[id^='onetidDoclibViewTbl']");
listViewTableId = $wpTableNode.attr('id');
// find the postback control id; e.g. ctl00$m$g_109629d4_d78b_4c9e_8ec0_90078b6e444e$ctl02
var
$wpinput = $wpnode.find('> :first-child > input:first-child'),
ctrlid = $wpinput.attr('id');
ctrlid = ctrlid && ctrlid.length
? ctrlid.replace(/^(ctl\d\d)_/gi,'$1$')
.replace(/_g_/g,'$g_')
.replace(/_(ctl\d\d)$/gi,'$$$1')
: '';
if (debug && window.console) { console.log(">>locateListView: controlid="+ctrlid); }
filterControlId = ctrlid;
viewModel.LibraryTitle(wpCtx.ListTitle);
viewModel.LibrarySubTitle('All');
viewModel.Working(false);
};
EnsureScript("inplview", typeof InitAllClvps, fn);
}
function filterByText() {
if (!filterControlId) {
alert('Unable to filter: the list view webpart could not be located or the list view contains a Person/Group column!');
return;
}
var filterText = this.FilterText();
if (debug && window.console) { console.log(">>FilterByText: ["+filterText+"]"); }
var $wpTableNode = $("#"+listViewTableId);
$wpTableNode.find("tr.ms-itmhover").each(function() {
var $tr = $(this);
if (filterText && filterText.length) {
var
r = new RegExp(filterText,'gi'),
matched = false;
$tr.find(".ms-vb-title,.ms-vb2").each(function() {
var td = $(this).text();
if(!r.test(td)) return true;
matched = true;
return false;
});
if(matched) $tr.fadeIn(250);
else $tr.fadeOut(250);
} else {
$tr.fadeIn(250);
}
});
}
function onFilterChange(filterType) {
if (!filterControlId) {
alert('Unable to filter: the list view webpart could not be located or the list view contains a Person/Group column!');
return;
}
var
filterColumn = null,
filterValue = '';
if (filterType.match(/year/gi)) {
filterValue = this.SelectedYear();
filterColumn = 'catYear';
} else if (filterType.match(/month/gi)) {
filterValue = this.SelectedMonth();
filterColumn = 'catMonth';
}
if (!filterValue) filterValue = '##dvt_all##';
var filterCall = "__doPostBack('"+filterControlId+"','NotUTF8;__filter={"+filterColumn+"="+filterValue+"}')"
if (debug && window.console) {
console.log(">>Filter by " + filterType + " on ["+filterColumn+"] = ["+filterValue+"]");
console.log(" >> " + filterCall);
}
eval(filterCall);
}
function start() {
ko.applyBindings(viewModel, $('#cdtm').get(0));
viewModel.FilterText.subscribe(filterByText, viewModel);
viewModel.SelectedYear.subscribe(onFilterChange.bind(viewModel, 'year'));
viewModel.SelectedMonth.subscribe(onFilterChange.bind(viewModel, 'month'));
locateListView();
}
}();
$(pd.FilterLibraryApp.start);
})({ Name: 'Module', LibraryTitle: 'CDLT Meetings' },jQuery);

CSS (2010)

/* CLEARFIX */
.cdtm-wrapper .row:before,
.cdtm-wrapper .row:after {
content: "";
display: table;
}
.cdtm-wrapper .row:after {
clear: both;
}
.cdtm-wrapper .row {
zoom: 1; /* For IE 6/7 (trigger hasLayout) */
}
.cdtm-wrapper .row .left {
float: left;
}
.cdtm-wrapper .row .right {
float: left;
}
/* LAYOUT */
.cdtm {
display: inline-block;
width: 100%;
}
.cdtm-wrapper {
font-family: 'Open Sans',Calibri,Verdana,Arial,'Sans Serif';
width: 100%;
font-size: 12px;
}
/* PADDING/MARGINS */
.cdtm-wrapper > .box-5 {
padding: 5px 10px 10px 0;
}
.cdtm-wrapper .header {
margin: 0;
padding: 0;
}
.cdtm-wrapper .nav {
margin: 0;
padding: 0;
}
.cdtm-wrapper article {
margin: 0;
padding: 0;
}
/* HEADER & NAVIGATION */
.cdtm-wrapper .header h1, .cdtm-wrapper .header h2, .cdtm-wrapper .header h3, .cdtm-wrapper .header h4, .cdtm-wrapper .header h5 {
color: #415A6C;
margin: 0 0 8px 0;
padding: 0;
font-size: 18pt;
line-height: 18pt;
font-weight: 700;
}
.cdtm-wrapper .header h2 {
font-size: 16pt;
}
.cdtm-wrapper .header h3 {
font-size: 14pt;
}
.cdtm-wrapper .header h4 {
font-size: 12pt;
}
.cdtm-wrapper .header h5 {
font-size: 12pt;
}
.cdtm-wrapper .header h1 *[class^='icon-'] {
font-size: 20px;
line-height: 20px;
}
.cdtm-wrapper .nav .left {
display: inline-block;
width: 60%;
min-width: 290px;
max-width: 410px;
}
.cdtm-wrapper .nav .right {
display: inline-block;
width: 40%;
max-width: 250px;
}
.cdtm-wrapper .nav ul {
list-style: none outside none;
margin: 0 0 5px;
padding: 0;
}
.cdtm-wrapper .nav ul.years {
font-size: 1.5em;
}
.cdtm-wrapper .nav ul.months {
font-size: 1.2em;
}
.cdtm-wrapper .nav li {
display: inline-block;
margin: 0;
padding: 0;
}
.cdtm-wrapper .nav li a, .cdtm-wrapper .nav li a:link {
color: #E24912;
display: inline-block;
padding: 2px 3px;
margin-right: 2px;
}
.cdtm-wrapper .nav li a:hover {
color: #002596;
cursor: pointer;
background-color: rgb(0,180,240);
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; /* IE 8/9 */
filter: alpha(opacity=50); /* IE 5.5 - 7 */
background-color: rgba(0,180,240,0.50);
}
.cdtm-wrapper .nav li.selected a,
.cdtm-wrapper .nav li.selected a:link {
background-color: rgb(0,180,240);
background-color: rgba(0,180,240,1);
color: whitesmoke;
}
/* SEARCH FIELD */
.cdtm-wrapper .nav .right .search-wrapper {
border: 1px solid #c8c8c8;
border-radius: 5px;
padding: 5px;
position: relative;
}
.cdtm-wrapper .nav .right .search-wrapper .searchfield {
background-color: #fff;
border: medium none;
border-radius: 3px;
color: #aeaeae;
display: inline-block;
font-size: 1.5em;
font-weight: normal;
margin: 0;
transition: all 0.2s linear 0s;
width: 88%;
}
.cdtm-wrapper .nav .right .search-wrapper .searchfield:focus {
color: #858585;
}
.cdtm-wrapper .nav .right .search-wrapper .icon-search {
color: #d5d8de;
display: inline-block;
font-size: 2em;
line-height: 10px;
position: absolute;
right: 6px;
text-indent: -3px;
width: 26px;
}
.cdtm-wrapper .nav .right .search-wrapper .icon-search:hover {
color: #415A6C;
cursor: pointer;
}

 

Published by

Phil Harding

SharePoint Consultant, Developer, Father, Husband and Climber.

22 thoughts on “Dynamically Filtering SharePoint List Views using Javascript

  1. Thanks for blogging about this solution! It works well to filter an OOTB List View Web Part in O365, but I could not get Ribbon integration on Item Selection working as-is. With the original solution, the Edit/View/Delete Ribbon Actions were always disabled when selecting filtered items. To address that issue, I changed the following line of code ” wpEvent = { clvp: clvp, event: { currentCtx: { clvp: clvp } } };” to ” wpEvent = { clvp: clvp, event: { currentCtx: clvp.ctx } };” and always call HandleFilter(wpEvent.event, filterUrl).

  2. Any way you have made this work with managed metadata columns? They involve a weird lookup structure.

  3. I figured out how to do it and it was a chore of talking to the terms tore and hidden taxonomy list but it works great! Cool post by the way!

  4. Thanks for the article. I’ve been waiting to integrate knockout with sharepoint. links to resources and or files do not seem to work though. i’m a litle confused at where the code to implement this is…

  5. @Phil: I found out. It is possible, but thanks anyway… Just do multiple postbacks in a row. Sharepoint saves the former filters and automatically combines them (with an AND-relationship)…

    But now I need a way to sort via postback…

  6. Does this work on library web parts? I saved all the files and adapted the html file for my environment URLs but after pointing the content editor at the html file it hangs at the progress loading .gif

  7. Hi and thanks for sharing. I made it work for SP2010 but I had to check the box “Show the actualize button” on the List View Webpart. Otherwise the controlid was always empty in the console.

  8. I would like to reuse the filter – but unfortunately under resource it is not possible to get the reference files.
    –> “HTML file – which includes the script and CSS files.”
    I would be very happy if i could test the filter by myself

  9. Great post. It would have been really helpful if someone could decouple this component from knockout (I am not familiar with knockout 😦 ). Do you have a plain JavaScript version of this? Thanks

  10. he had before a regular plain javascript, for example:
    …(alll options)

    //Attach change event handler to filter dropdown
    $(document.body).on(‘change’, ‘#filter’, function () {
    CSWPFilterByField(‘owstaxIdSourceMMD’, $(‘#filter’).val(), ‘contain’));
    });

    function CSWPFilterByField(_fldName, _fldVal, _fldOperator)
    {
    // Loop through Query Groups
    var groups = Srch.ScriptApplicationManager.get_current().queryGroups;

    $.each(groups, function () {
    // Look for query groups associated with a CSWP
    if (this.displays != null && this.displays.length > 0) {
    if (this.displays[0] instanceof Srch.ContentBySearch) {
    // Update and execute query – change length according to specific ToolPart query!!
    if (_fldVal == “All”) {
    var newQuery = this.dataProvider.get_queryTemplate().substring(0, 42);
    }
    else {
    var operator = (_fldOperator == ‘equal’) ? ‘=’ : ‘:’;
    var newQuery = this.dataProvider.get_queryTemplate().substring(0, 42) + _fldName + operator + _fldVal;
    }
    this.dataProvider.set_queryTemplate(newQuery);
    this.dataProvider.issueQuery();
    //Saving value globally for next filtering
    window.option = $(‘#filter’).find(‘option:selected’).val();
    }
    }
    });
    }

    good luck!
    a programmer

  11. Hey, thank you very much for sharing this cool gimmick! Is it possible to have multiselect here? e. g. month Jan + Feb + Mar This would be great 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.