Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 52 additions & 11 deletions jade/page-contents/autocomplete_content.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
</div>
</div>

<pre><code class="language-markup">
<pre style="padding-top: 0px;">
<span class="copyMessage">Copied!</span>
<i class="material-icons copyButton">content_copy</i>
<code class="language-markup copiedText">
&lt;div class="row">
&lt;div class="col s12">
&lt;div class="row">
Expand All @@ -33,15 +36,19 @@
&lt;/div>
&lt;/div>
&lt;/div>
</code></pre>
</code>
</pre>
</div>

<div id="initialization" class="scrollspy section">
<h3 class="header">Initialization</h3>
<p>The data is a json object where the key is the matching string and the value is an optional image url.</p>
<p>The key must be a text string. If you trust your data, or have properly sanitized your user input, you may
use HTML by setting the option <code class="language-javascript">allowUnsafeHTML: true</code>.</p>
<pre><code class="language-javascript">
<pre style="padding-top: 0px;">
<span class="copyMessage">Copied!</span>
<i class="material-icons copyButton">content_copy</i>
<code class="language-javascript copiedText">
document.addEventListener('DOMContentLoaded', function() {
var elems = document.querySelectorAll('.autocomplete');
var instances = M.Autocomplete.init(elems, {
Expand All @@ -67,7 +74,8 @@ <h3 class="header">Initialization</h3>
},
});
});
</code></pre>
</code>
</pre>
</div>


Expand Down Expand Up @@ -109,10 +117,16 @@ <h3 class="header">Options</h3>
<td>Minimum number of characters before autocomplete starts.</td>
</tr>
<tr>
<td>sortFunction</td>
<td>compareFunction</td>
<td>Function</td>
<td></td>
<td>Sort function that defines the order of the list of autocomplete options.</td>
<td>A function that compares two values and decides which goes first.</td>
</tr>
<tr>
<td>customSort</td>
<td>Function</td>
<td></td>
<td>A function that defines the order of the list of autocomplete options.</td>
</tr>
<tr>
<td>allowUnsafeHTML</td>
Expand All @@ -130,18 +144,41 @@ <h3 class="header">Options</h3>
</table>

<h5 class="method-header">
sortFunction
compareFunction
</h5>
<p>This is the default compareFunction. You can write your own compareFunction by passing in a function with these same
3 parameters. You can read more about how a compareFunction works
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort">here</a>.</p>
<pre><code class="language-javascript col s12">
// Sort function for sorting autocomplete results
// Compare function for sorting autocomplete results
function (a, b, inputString) {
return a.indexOf(inputString) - b.indexOf(inputString);
}
</code></pre>
</code>
</pre>
<p>To disable sorting and use the values as they appear in the data object, use a falsy value.</p>

<h5 class="method-header">
customSort
</h5>
<p>A custom sort function takes 2 arguments, an object of all the elements in the autocomplete and the user input.
This function should return an array of objects.
<pre><code class="language-javascript col s12">
// Example of an input object
{
"Apple":null,
"Microsoft":null,
"Google":"https://placehold.it/250x250"
}

// Example of an output array
[
{"data":null,"key":"Apple"},
{"data":"https://placehold.it/250x250","key":"Google"}
]
</code></pre>
This allows you to do things such as implmenting a fuzzy search using <a href="https://fusejs.io/">Fuse.js</a> or similar libraries.
An implementation of this can be found here: <a href="https://github.com/materializecss/materialize/blob/v1-dev/test/html/autocomplete.html">materialize/autocomplete.html</a></p>
</div>


Expand All @@ -150,7 +187,10 @@ <h3 class="header">Methods</h3>
<blockquote>
<p>Because jQuery is no longer a dependency, all the methods are called on the plugin instance. You can get the plugin
instance like this: </p>
<pre><code class="language-javascript col s12">
<pre style="padding-top: 0px;">
<span class="copyMessage">Copied!</span>
<i class="material-icons copyButton">content_copy</i>
<code class="language-javascript col s12 copiedText">
var instance = M.Autocomplete.getInstance(elem);

/* jQuery Method Calls
Expand All @@ -160,7 +200,8 @@ <h3 class="header">Methods</h3>
$('.autocomplete').autocomplete('methodName');
$('.autocomplete').autocomplete('methodName', paramName);
*/
</code></pre>
</code>
</pre>
</blockquote>
<h5 class="method-header">
.open();
Expand Down
57 changes: 36 additions & 21 deletions js/autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
coverTrigger: false
},
minLength: 1, // Min characters before autocomplete starts
sortFunction: function(a, b, inputString) {
compareFunction: function(a, b, inputString) {
// Sort function for sorting autocomplete results
return a.indexOf(inputString) - b.indexOf(inputString);
},
Expand Down Expand Up @@ -363,29 +363,44 @@

let matchingData = [];

// Gather all matching data
for (let key in data) {
if (data.hasOwnProperty(key) && key.toLowerCase().indexOf(val) !== -1) {
let entry = {
data: data[key],
key: key
};
matchingData.push(entry);
if (this.options.customSort) {
const customResult = this.options.customSort(data, val.toLowerCase());

this.count++;
// Check to make sure the result is an array
if (Array.isArray(customResult)) {
// If it is, use it
matchingData = customResult;
} else {
// If not, throw an error
console.error(
'customSort should return an array objects. For more info see https://materializecss.github.io/materialize/autocomplete.html'
);
}
} else {
// Gather all matching data
for (let key in data) {
if (data.hasOwnProperty(key) && key.toLowerCase().indexOf(val) !== -1) {
let entry = {
data: data[key],
key: key
};
matchingData.push(entry);

this.count++;
}
}
}

// Sort
if (this.options.sortFunction) {
let sortFunctionBound = (a, b) => {
return this.options.sortFunction(
a.key.toLowerCase(),
b.key.toLowerCase(),
val.toLowerCase()
);
};
matchingData.sort(sortFunctionBound);
// Sort
if (this.options.compareFunction) {
let compareFunctionBound = (a, b) => {
return this.options.compareFunction(
a.key.toLowerCase(),
b.key.toLowerCase(),
val.toLowerCase()
);
};
matchingData.sort(compareFunctionBound);
}
}

// Limit
Expand Down
36 changes: 34 additions & 2 deletions test/html/autocomplete.html
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,19 @@ <h5>allowUnsafeHTML: true</h5>
</div>
</div>

<h5>customSort: Fuse.js</h5>
<div class="row">
<div class="col s12">
<div class="row">
<div class="input-field col s12">
<i class="material-icons prefix">textsms</i>
<input type="text" id="autocomplete-6" class="autocomplete">
<label for="autocomplete-6">Filter Function</label>
</div>
</div>
</div>
</div>

<!--
<h5>Custom dropdown options</h5>
<div class="row">
Expand Down Expand Up @@ -136,6 +149,7 @@ <h5>Pre-populate chips</h5>
<!-- Scripts-->
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="../../bin/materialize.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fuse.js/6.4.6/fuse.min.js" integrity="sha512-KnvCNMwWBGCfxdOtUpEtYgoM59HHgjHnsVGSxxgz7QH1DYeURk+am9p3J+gsOevfE29DV0V+/Dd52ykTKxN5fA==" crossorigin="anonymous"></script>
<script>
const defaultData = {
"Apple": null,
Expand All @@ -144,18 +158,21 @@ <h5>Pre-populate chips</h5>
};

const bigData = {};
const bigDataKeys = [];
for (let i = 200; i >= 0; i--) {
const randString = 'a' + Math.random().toString(36).substring(2);
bigData[randString] = null;
bigDataKeys.push(randString)
}
const fuse = new Fuse(bigDataKeys)

const a1 = {
data: defaultData
};

const a2 = {
data: defaultData,
filterFunction: function (key_string, filter_string) {
compareFunction: function (key_string, filter_string) {
return true;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think returning true for a compareFunction is a bit weird. But it made sense for a thing called filterFunction. Don't you think?
Why did this line change?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To my knowledge, filterFunction never existed, this is the only reference to it I can find. I'd consider using the name over compareFunction as I agree that it makes a bit more sense. Filter function to me, however does imply that things would be removed from the results but that's not the case. Maybe @samschurter could give some insight as they introduced this line (infact the whole file).

}
};
Expand All @@ -181,15 +198,30 @@ <h5>Pre-populate chips</h5>
allowUnsafeHTML: true
}

const a6 = {
data: bigData,
customSort: function (list, search) {

let toDraw = []
const out = fuse.search(search)
for (const key in out) {
toDraw.push( {data:list[out[key].item], key:out[key].item});
}

return toDraw
}
}

document.addEventListener('DOMContentLoaded', function () {
M.Autocomplete.init(document.getElementById('autocomplete-1'), a1);
M.Autocomplete.init(document.getElementById('autocomplete-2'), a2);
M.Autocomplete.init(document.getElementById('autocomplete-3'), a3);
M.Autocomplete.init(document.getElementById('autocomplete-4'), a4);
M.Autocomplete.init(document.getElementById('autocomplete-5'), a5);
M.Autocomplete.init(document.getElementById('autocomplete-6'), a6);
});

</script>
</body>

</html>
</html>