Javascript-форум (https://javascript.ru/forum/)
-   Элементы интерфейса (https://javascript.ru/forum/dom-window/)
-   -   Живой поиск. Как реализовать фичу (https://javascript.ru/forum/dom-window/65687-zhivojj-poisk-kak-realizovat-fichu.html)

veg 02.11.2016 21:42

Живой поиск. Как реализовать фичу
 
Вложений: 1
На страничке стоит живой поиск, который начинает делать выборку по городам при вводе двух символов.

Все работает, все отправляется. Но есть одно НО, нужно чтоб люди могли ТОЛЬКО выбирать из предлагаемого списка вариантов.

Если совпадений не найдено,то в выводимых результатах появлялась надпись - СОВПАДЕНИЯ НЕ НАЙДЕНЫ, а при смене фокуса с поля input вводимые данные уничтожаются.

<form method="POST" action=""> 
    <input type="text" name="referal" placeholder="Живой поиск" value="" class="who"  autocomplete="off"><input type="submit" name="subscribe" value="Отправить!"> 
    <ul class="search_result" id='popup'></ul>
</form>

<script>
jQuery(function($){
	$(document).mouseup(function (e){ // событие клика по веб-документу
		var div = $("#popup"); // тут указываем ID элемента
		if (!div.is(e.target) // если клик был не по нашему блоку
		    && div.has(e.target).length === 0) { // и не по его дочерним элементам
			div.hide(); // скрываем его
		}
	});
}); </script>


if(!empty($_POST["referal"])){ //Принимаем данные

    $referal = trim(strip_tags(stripcslashes(htmlspecialchars($_POST["referal"]))));

    $db_referal = $mysqli -> query("SELECT * from ".PREFIX."city WHERE city LIKE '%$referal%'")
    or die('Ошибка №'.__LINE__.'<br>Обратитесь к администратору сайта пожалуйста, сообщив номер ошибки.');

    while ($row = $db_referal -> fetch_array()) {
        echo "\n<li>".$row["city"]."</li>"; //$row["name"] - имя таблицы
    }

}

$(function(){
    
    //Живой поиск
    $('.who').bind("change keyup input click", function() {
        if(this.value.length >= 2){
            $.ajax({
                type: 'post',
                url: "search.php", //Путь к обработчику
                data: {'referal':this.value},
                response: 'text',
                success: function(data){
                    $(".search_result").html(data).fadeIn(); //Выводим полученые данные в списке
                }
            })
        }
    })
    
    $(".search_result").hover(function(){
        $(".who").blur(); //Убираем фокус с input
    })
    
    //При выборе результата поиска, прячем список и заносим выбранный результат в input
    $(".search_result").on("click", "li", function(){
        s_user = $(this).text();
		$(".search_result").fadeOut();
        $(".who").val(s_user); //деактивируем input, если нужно
        
    })

})

laimas 03.11.2016 01:31

$referal = trim(strip_tags(stripcslashes(htmlspecialchars($_P OST["referal"]))));

Кашу маслом не испортишь? htmlspecialchars применяется при выдаче данных клиенту чтобы избежать на клиенте XSS атак или банально не добавить ошибок в html. strip_tags, к сведению, может работать некорректно, и запрос типа "<div>1</div>" никакой опасности не представляет.

Функция stripslashes удаляет экранирование и применяется, чтобы исключить действие магических кавычек, которые надо раз и навсегда отключить в конфигурации, а stripcslashes удаляет экранирование стиля С. В тоже время, хотя в запросах городов такового не может быть, но уж коли со страха режутся теги и замена на html-сущности, то надо учитывать то, что LIKE использует символы _ и % как спецсимволы, которые нужно экранировать в данных посредством addcslashes().

То есть все что понаписано в запросе никоим образом не спасет от sql-инъекции. Для этого в оригинальном расширении MySQL применяется mysql_real_escape_string, а mysqli mysqli_real_escape_string, но в mysqli лучше использовать подготовленные запросы (prepare) и метку параметров ?, а экранированием займется драйвер.

По ответу клиента строится список. А что будет у клиента при получении 'Ошибка №'.__LINE__.'<br>Обратитесь к администратору сайта пожалуйста, сообщив номер ошибки.'? И вообще, если таким образом администратор будет разбираться с ошибками, то он поседеет, ибо скриптов много и одноименных линий в них куча. А пользователи не обязательно программисты, и не обязательно точно смогут описать где и при каких обстоятельствах произошло нечто.

Может сперва исправите код на человеческий, измените логику общения клиент-сервер, а потом уже пояснить, что означает "люди могли ТОЛЬКО выбирать из предлагаемого списка вариантов", ибо что кроме выбора они в нем могут сделать? Иначе, если понимать это буквально, то ни LIKE, ни ввода на клиенте не нужно, и все должно быть иное.

К сведению - echo "\n<li>".$row["city"]."</li>" - для html это просто мусор.

veg 03.11.2016 03:20

Вложений: 1
Вот это письмецо.
Это же скрипт на столько упрощенный, чтобы вопросов не возникало. Конечно же ошибка тоже приведена для примера.

По поводу самого вопроса - "люди могли ТОЛЬКО выбирать из предлагаемого списка вариантов"
Означает, что люди могут выбирать город, только из предлагаемого списка вариантов. Если вариант не находится, то как во вложении выше выдается надпись - совпадений не найдено, а поле input очищается.
А кроме выбора они могут сделать следующее:
Вбить несуществующий город и нажать кнопку отправить. Собственно вот что и вышло, во вложении ниже.
Вложение 3334

laimas 03.11.2016 04:03

Цитата:

Сообщение от veg
Это же скрипт на столько упрощенный, чтобы вопросов не возникало.

trim(strip_tags(stripcslashes(htmlspecialchars($_P OST["referal"])))) - это не упрощение, а бесполезное усложнение, как следствие непонимания назначения указанных функций.

Насчет ошибки - ul кроме li ничего не должен содержать, а вы строя список на нем конечно его стилизуете. Как можно при этом вместо списка в ul втыкать сообщение об ошибке? Ведь проблема не в том, что вы номера строк в файлах сообщаете, а в том как сообщаете об ошибках, код то клиентский просто принимает и помещает. Ну разве же так делается, а если так не делается, то где реальный код?

А чтобы выбирать из списка имеющегося, то код то ведь совсем иной будет при этом. Тут только возникает вопрос - а сколько позиций предлагает список, будет ли удобен при этом выбор в нем? Это ведь тоже надо обдумать, а если обдумать, то это несомненно скажется на коде как клиента, так и сервера. Или не так?

То есть ваш вопрос, это нечто общее, желание, а детализация этого желания отсутствует. Значит и ответ может быть в общих чертах. )

veg 03.11.2016 11:38

http://02r.ru/news/ вот как это выглядит, чтоб было совсем понятно.
Вариантов около 1000, но пользователю предоставляется выбор. Можно ввести несколько букв города и выбрать из списка нужны, а можно просто ввести несуществующий и отправить форму.

Нужно, чтобы при отсутствии вариантов выводилась строка - совпадений нет https://vk.com/edit?act=contacts как здесь. Попробуйте вбить несуществующий город.

Гробовщик 03.11.2016 13:10

veg,
Ну считай сколько у тебя записей из базы возвращается... Если 0 то выводи сообщение.
Что бы отчистить город как вариант можно сделать поле input type="hidden" в котором будет меняться значение в зависимости от выбора города (ну как пример).
А вообще самый оптимальный вариант проводить проверку города после отправки на сервер (так то она должна быть при лбом раскладе) и если пользователь проигнорировал сообщение "нет такого города", то он сам виноват, пускай перезаполняет форму заново

laimas 03.11.2016 13:28

Цитата:

Сообщение от veg
Вариантов около 1000, но пользователю предоставляется выбор. Можно ввести несколько букв города и выбрать из списка нужны, а можно просто ввести несуществующий и отправить форму.

Живой поиск (или плагины AutoComplete) потому и называется живым, что находит совпадения по мере ввода.

Вы же в вопросе написали буквально следующее "люди могли ТОЛЬКО выбирать из предлагаемого списка вариантов", а это уже не поиск, а предложения, то есть есть готовый список, выбирайте, а нет вводите свое. Но если нет в предложении, то ввод "другого", это тоже не поиск, а добавление нового субъекта в базу, который впоследствии может предлагаться.

Как еще иначе понимать ваше требование? А если понимание правильное, то список предложений из 1000 городов, это уже много, думаю не стоит объяснять, что выбор в таком будет очень неудобным. В тоже время существует множество готовых плагинов AutoComplete, включая и с постраничной навигацией в списке, и прочими плюшками.

Так что решайте, любо ищите готовое подходящее, либо отказываетесь от "только выбор из списка" и все-таки живой поиск, либо хорошо обдумайте "все подводные камни всех И", и тогда формулируйте вопрос.

Цитата:

Сообщение от veg
Попробуйте вбить несуществующий город.

Не буду пробовать: а) я и так прекрасно знаю как работает AutoComplete; б) конкретно на ВК посмотреть не могу - не был и не буду участником соцболот, а регистрироваться у них ради пощелкать тем более. )

veg 03.11.2016 15:54

Цитата:

Сообщение от Гробовщик (Сообщение 433886)
veg,
Ну считай сколько у тебя записей из базы возвращается... Если 0 то выводи сообщение.
Что бы отчистить город как вариант можно сделать поле input type="hidden" в котором будет меняться значение в зависимости от выбора города (ну как пример).
А вообще самый оптимальный вариант проводить проверку города после отправки на сервер (так то она должна быть при лбом раскладе) и если пользователь проигнорировал сообщение "нет такого города", то он сам виноват, пускай перезаполняет форму заново

Спасибо. Позже отпишу что получилось.

veg 03.11.2016 22:56

Цитата:

Сообщение от laimas (Сообщение 433887)
Живой поиск (или плагины AutoComplete) потому и называется живым, что находит совпадения по мере ввода.

Так он совпадения и находит, пользователь должен выбрать из совпавшего, как в яндексе, зайдите поглядите, там регистрироваться не нужно. Если я ввожу например слово javascript в яндексе, то он предлагает мне несколько вариантов совпадений, при клике на который уже заполняется поле input. Далее нажимаю кнопку поиск и он выдает результаты по моему запросу.

Если же я например введу слово jjjjaavvaascripttt, то в выпадающем поле ничего не будет, а при дальнейшем нажатии кнопки ИСКАТЬ тоже вряд ли что-то найдет.


Цитата:

Сообщение от laimas (Сообщение 433887)
Вы же в вопросе написали буквально следующее "люди могли ТОЛЬКО выбирать из предлагаемого списка вариантов", а это уже не поиск, а предложения, то есть есть готовый список, выбирайте, а нет вводите свое. Но если нет в предложении, то ввод "другого", это тоже не поиск, а добавление нового субъекта в базу, который впоследствии может предлагаться.

А яндекс что не предлагает? Когда в поисковике вводите фразу, он и выдает Вам совпадения из списка часто используемых поисковых фраз. Добавлять в базу новый город через форму мне не нужно.
Цитата:

Сообщение от laimas (Сообщение 433887)
Как еще иначе понимать ваше требование? А если понимание правильное, то список предложений из 1000 городов, это уже много, думаю не стоит объяснять, что выбор в таком будет очень неудобным. В тоже время существует множество готовых плагинов AutoComplete, включая и с постраничной навигацией в списке, и прочими плюшками.

Чем глаголить в пустую не разобравшись, открыли бы пример, а если нечего по делу сказать, лучше даже не пишите.

Еще раз, откройте яндекс, вбейте слово javascript, он вам выдаст с десяток часто используемых поисковых фраз(при клики на любое заполнится поле input), а их явно не 1000, скажу больше, даже не 1000 000, а вы говорите что 1000 городов - это уже много.
Цитата:

Сообщение от laimas (Сообщение 433887)
Так что решайте, любо ищите готовое подходящее, либо отказываетесь от "только выбор из списка" и все-таки живой поиск, либо хорошо обдумайте "все подводные камни всех И", и тогда формулируйте вопрос.

Какие камни? Я ж не собираюсь корпорацию открывать. Взламывать мой доходяшный сайтик вряд кто будет, его еще сперва найти надо.
Цитата:

Сообщение от laimas (Сообщение 433887)
Не буду пробовать: а) я и так прекрасно знаю как работает AutoComplete; б) конкретно на ВК посмотреть не могу - не был и не буду участником соцболот, а регистрироваться у них ради пощелкать тем более. )

a) autocomplete у меня вообще OFF стоит.
B) Я не ясновидец, не знаю есть страничка у Вас или нет.

veg 04.11.2016 00:26

$i=0;
	
    while ($row = $db_referal -> fetch_array()) { $i++;
		
        echo "\n<li><span>".$row["city"]."</span></li>"; //$row["name"] - имя таблицы
    } 
	if($i == 0){echo "\n<li>Нет такого города</li>";}


Сделал так. Теперь выводит что город не найден и это поля неактивно для выбора.
$(".search_result").on("click", "li span", function(){
        s_user = $(this).text();
		$(".search_result").fadeOut();
        $(".who").val(s_user); 
        
    })

Теперь осталось сделать, чтобы поле input очищалось при условии, что город не найден и что был клик вне этого input.

laimas 04.11.2016 12:51

Все о Яндексе, все это живой поиск. Но не надо сравнивать поиск Яндекса в своем кеше или в интернете, с вашим списком городов, это большая разница. Яндекс имеет право предложить на его взгляд подходящее, а вы нет, ибо у вас выбор ограничивается строгим набором.

Из всего сказано ясно, что именно живой поиск вам и нужен, а "могли ТОЛЬКО выбирать из предлагаемого списка вариантов", это и есть ваше ограничение, и имеет иной смысл.

Цитата:

Сообщение от veg
autocomplete у меня вообще OFF стоит.

Это атрибут формы/полей, а таким же образом называют и плагины которые и занимаются живым поиском, Их наплодили уже достаточно много, можете для интереса найти и просмотреть их демонстрации.

А теперь по вашему.

Города в базе нужно держать в отдельной таблице под уникальным идентификатором каждый. Вывод списка городов, это вывод названия города как текста опции, а его идентификатора как ее значения. Другими словами, в вашем случае лучше иметь костомизированный список, который будет работать в фонов режиме и отправлять на сервер выбор пользователя, а список UL отображать данные. Можно конечно поступить так как и у вас, но LI должны иметь в атрибутах идентификатор городов, которые передаются в скрытое поле при выборе, которое будет отправлять серверу выбор пользователя.

Если пользователь может выбрать только из предлагаемого, то скрытое поле должно очищаться уже при получении фокуса полем ввода поиска.

Если пользователь осведомлен, что он не может ввести произвольный город, а список предложений будет только в случае найденного, то сообщать что ничего не найдено большого смысла нет. Просто списка естественно не будет в этом, что и есть результат "не найдено", разве это не понятно и без сообщения?

$i=0;
while ($row = $db_referal -> fetch_array()) {
$i++;

Зачем этот не нужный счетчик? Сам результат запроса уже отвечает на этот вопрос - есть ли записи или нет - $db_referal->num_rows(). Если вернется более 0, значит есть совпадения, и сразу отдаем клиенту весь набор как JSON, если вместо $db_referal->fetch_array() использовать $db_referal->fetch_all():

$db_referal = $mysqli -> query("SELECT field_id, field_name from ".PREFIX."city WHERE city LIKE '%$referal%'"); //надеюсь баг с дырой будет исправлен
//отдаем клиенту результат и выход
exit(json_encode($db_referal->num_rows() ? $db_referal->fetch_all() : null));
//или проще, так как если не будет найдено то будет возвращен пустой массив
//и проверять количество возвращенный записей нет необходимости
exit(json_encode($db_referal->fetch_all() ?: null));


На клиенте:

//response выбросить
dataType: 'json',
success: function(data){
                    if($data) //строим и показываем список по полученным данным
                    else //ничего не найдено, списка не будет
                }


И еще раз - отдавать клиенту html с намеренно добавленным \n, кстати в Win, это \r\n (в РНР для этого есть константа PHP_EOL, она сама разберется что конкретно на платформе) это просто бессмысленно, мусор это. Идеальный html, это одна длиииииинная строка, а структуру его всегда можно просмотреть в отладчике.

veg 04.11.2016 14:50

Вложений: 1
Цитата:

Города в базе нужно держать в отдельной таблице под уникальным идентификатором каждый. Вывод списка городов, это вывод названия города как текста опции, а его идентификатора как ее значения.
Так и есть. У каждого города есть свой идентефикатор, но это следующим шагом хотел сделать. До это был выпадающий список городов (<select>), но так как количество городов около 1000, и листать в поисках нужного не есть хорошо, решил прибегнуть к варианту из-за которого собственно тема и открылась. Пользователь мог выбрать город, а обработчику передавался id этого города.
Вложение 3337
Цитата:

Если пользователь осведомлен, что он не может ввести произвольный город, а список предложений будет только в случае найденного, то сообщать что ничего не найдено большого смысла нет. Просто списка естественно не будет в этом, что и есть результат "не найдено", разве это не понятно и без сообщения?
Пользователь не осведомлен. Он узнает об этом только тогда, когда выйдет сообщение о том, что город не найден.

Получилось следующее. Реакции на ввод данных нет.
$db_referal = $mysqli -> query("SELECT id,city from ".PREFIX."city WHERE city LIKE '%$referal%'");

 exit(json_encode($db_referal->fetch_all() ?: null));


data: 'json',
                success: function(data){
                     //Выводим полученные данные в списке
					 if($data) {$(".search_result").html(data).fadeIn();}//строим и показываем список по полученным данным
					 else {}//ничего не найдено, списка не будет

                }

laimas 04.11.2016 15:27

Заходим на страничку Гугла, Яшки, Бинг и прочих, начинаем поиск - есть ли список предложений, если робот ничего не найдет? Тоже самое и у вас. А пользователь должен знать сразу, что он может выбрать только тот город, который у вас оговорен условиями. Живой же поиск, это как раз сервис, чтобы не выводить длинную портянку, и не иметь представления о том как он работает в данное время может только пьяный ежик. )
Хотите еще и контрольный выстрел, ну так причем тут не найдено? Тогда уже сообщайте пользователю, что данный город (введенный пользователем) ваш сайт не принимает/не одобряет/или иная какая-то причина, иначе как же он узнает почему не может ввести произвольное? Уж как-то надо поставить в известность бедного пользователя.

Цитата:

Сообщение от veg
Реакции, на ввод данных, нет.

И не будет - я вам показал кодом, что сервер возвращает не готовый html-список, а json-строку, которую jQuery преобразует в объект. Это в случае если будут найдены совпадения. Объект (data) - это будет массив объектов, то есть запрос возвращает id и city поля, и объект будет например такой:

data = [
  {"id" : 12, "city" : "Волгоград"},
  {"id" : 15, "city" : "Воронеж"},
   ......
]


Этот массив объектов нужно обойти циклом и построить по ним список. Или не известно как это делается, да еще посредством JQ?

А если data пуст, значит нет и списка, а уж надо сообщать и что при этом решайте сами.

Примечание: если JSON и показанный код ответа, то fetch_all(MYSQLI_ASSOC). Не используйте fetch_array() если в этом нет необходимости, это на будущее.

Чтобы объект был именно таким {"id" : 12, "city" : "Волгоград"}, нужно указать параметры кодирования:

exit(json_encode($db_referal->num_rows() ? $db_referal->fetch_all() : null, JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE));


если они доступны - первый с 5.3.3, второй с 5.4.0

veg 04.11.2016 16:05

С json работал, только средствами php все обрабатывал.
Сделал так. Все выводится, сейчас буду допиливать.

$(function(){
    
    //Живой поиск
    $('.who').bind("change keyup input click", function() {
        if(this.value.length >= 2){
            $.ajax({
                type: 'post',
                url: "search.php", //Путь к обработчику
                data: ({'referal':this.value,'id':this.id}),
                response: 'text',
                success: function(data){
                    $(".search_result").html(data).fadeIn(); //Выводим полученые данные в списке
                }
            })
        }
		
		
    })
    
    $(".search_result").hover(function(){
        $(".who").blur(); //Убираем фокус с input
    })
    
    //При выборе результата поиска, прячем список и заносим выбранный результат в input
    $(".search_result").on("click", "li span", function(){
        s_user = $(this).value;
		s_id =  $(this).id;
		$(".search_result").fadeOut();
        $(".who").val(s_user); //деактивируем input, если нужно
        
    })

})


$db_referal = $mysqli -> query("SELECT id,city from ".PREFIX."city WHERE city LIKE '%$referal%'");
    
	
	exit(json_encode($db_referal->fetch_all() ?: null,JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE));

laimas 04.11.2016 16:58

Пусть сервер вернул показанное в примере на ввод "во", стоим список:

<html> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script> 
$(function() {
//это вернул сервер в success
var data = [
  {"id" : 12, "city" : "Волгоград"},
  {"id" : 15, "city" : "Воронеж"}
];
//success обработчик
var ul = $('ul.search_result');
$.each(data, function() {
    ul.append('<li data-id="' + this.id + '">' + this.city + '</li>')
})
});
</script>     
</head> 
<body>
<ul class="search_result"></ul>
</body> 
</html>


Список строится?

Почему без ID? Или вы намерены вместо идентификаторов использовать имена? В скрытое поле нужно передвать идентификатор. А можно и в LI спрятать радио кнопки с label, и значение у которых есть идентификаторы, значение выбранной кнопка будет отправлено серверу.

veg 04.11.2016 17:12

Вложений: 1
Да. Список выводится в таком формате.
Вложение 3338
ID хочу занести в скрытый input

laimas 04.11.2016 17:45

Цитата:

Сообщение от veg
ID хочу занести в скрытый input

Ну так чтобы его занести по выбору пользователя, его же нужно получить и получить выбранного города, а значит идентификаторы городов нужно помещать в атрибуты списка, например как я показал. У вас же не видно, что они вообще используются.

veg 04.11.2016 18:05

Ну список выше прислал, ID в li занесен будет конечно, я пока разбираюсь с json.
Не выводит список когда добавил Ваш код.
url: "search.php", //Путь к обработчику
                data: ({'referal':this.value,'id':this.id}),
                
                success: function(){
					
					var ul = $('ul.search_result');
						$.each(data, function() {
						ul.append('<li data-id="' + this.id + '">' + this.city+ '</li>')

						});
                    $(".search_result").html(data).fadeIn(); //Выводим полученые данные в списке

laimas 04.11.2016 18:54

Поиск в базе осуществляется по введенному пользователем тексту, откуда у "неизвестного еще для сервера" может оказаться ID?

data: ({'referal':this.value,'id':this.id}) - с чего это тут появилось и откуда?

Полученное нужно обработать, data, это аргумент функции обработчика, куда он исчез? То что у меня в примере это объявлено как глобальная переменная, так это только для примера, чтобы его можно было запустить для просмотра.

dataType : 'json', //обязательно или если не указывать, то сервер должен передавать соответствующий заголовок
success: function(data){
    if(data) { //если найдены совпадения 
        var ul = $('ul.search_result').empty().show(); //эффекты типа fadeIn() вряд ли уместны здесь
        //это и есть добавление в список
        $.each(data, function() {
            ul.append('<li data-id="' + this.id + '">' + this.city+ '</li>')
        });
        //а $(".search_result").html(data) при том что data есть объект, это чушь
        //о fadeIn() выше сказано
    } else {
        //действия в случае если не найдено совпадений
    }
}


Часовой пояс GMT +3, время: 03:03.