login  Naam:   Wachtwoord: 
Registreer je!
 Forum

autocomplete probeersel (jQuery+AJAX+PHP) - suggesties?

Offline Thomas - 26/11/2013 13:51
Avatar van ThomasModerator Bij wijze van oefening heb ik zelf geprobeerd een autocomplete dropdown te maken met behulp van jQuery (gebruikt o.a. AJAX) in combinatie met PHP. Er zullen vast legio out-of-the-box oplossingen zijn, maar dit wiel is van mij . De vraag is nu, wat kan hier nog aan veranderd/verbeterd worden? Ik heb zo snel geen manier gevonden om de (eenvoudige) toetsenbordnavigatie zo te maken dat deze een muisbesturing simuleert, weet ook niet of dit e.e.a. makkelijker maakt. Om dit script uit te kunnen voeren heb je PHP-ondersteuning nodig (voor het afhandelen van de AJAX-call).

In mijn opzet was mijn voornaamste doel (naast het opfrissen van mijn kennis) het zoveel mogelijk beperken van (overbodige) AJAX-calls. Daarnaast ging het mij om de herbruikbaarheid en flexibiliteit: je kunt meerdere autocomplete-velden in eenzelfde formulier stoppen, dit zal werken. Ook mag de AJAX-URL querystring-variabelen (bijvoorbeeld voor authenticatie in het script etc.) bevatten, hier wordt de zoekstring-variabele via jQuery netjes aan vast geplakt.

Ik denk dat alles redelijk voor zich spreekt; motivaties voor andere manier van aanpakken zijn welkom.

voorbeeld van gebruik (jquery.dropdown.htm)
  1. <!--
  2. // @todo simulate mouse instead of keyboard?
  3. -->
  4. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  5. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  6. <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
  7. <script src="jquery.dropdown.js" type="text/javascript"></script>
  8. <link href="jquery.dropdown.css" rel="stylesheet" type="text/css" />
  9. </head>
  10.  
  11. <input type="text" id="inputtest" class="autocomplete" value="" /><br />
  12. <div id="boxtest" class="autocomplete-box"></div>
  13. <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
  14. <p>&nbsp;</p>
  15. <input type="text" id="inputtest2" class="autocomplete" value="" /><br />
  16. <div id="boxtest2" class="autocomplete-box"></div>
  17. <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
  18. <script type="text/javascript">
  19. //<![CDATA[
  20. $().ready(function() {
  21. var box = new autocompleteBox();
  22. box.init({
  23. 'inputId': 'inputtest',
  24. 'boxId': 'boxtest',
  25. 'ajaxURL': 'jquery.dropdown.php',
  26. 'ajaxQueryStringVariable': 'q' // try "z" (obviously will not return any results)
  27. });
  28.  
  29. var box2 = new autocompleteBox();
  30. box2.init({'inputId': 'inputtest2', 'boxId': 'boxtest2', 'ajaxURL': 'jquery.dropdown.php'});
  31. });
  32. //]]>
  33. </body>
  34. </html>


De CSS (jquery.dropdown.css)
  1. @CHARSET "UTF-8";
  2. input.autocomplete { width: 195px; height: 25px; line-height: 25px; border: 1px solid #000000; margin: 0; padding: 0 0 0 5px; }
  3. div.autocomplete-box { position: absolute; z-index: 1; width: 200px; border-width: 0px 1px 1px 1px; border-style: solid; border-color: #ff0000; background-color: #ffffff; }
  4. div.autocomplete-box-row { display: block; width: 200px; height: 25px; line-height: 25px; overflow: hidden; }
  5. div.autocomplete-box-row a { display: block; width: 100%; height: 100%; padding-left: 5px; }
  6. /* let jQuery handle :hover state as well, to prevent "sticky" hover */
  7. div.autocomplete-box-row a.hover { background-color: #ff0000; }


De jabbascript (jquery.dropdown.js)
  1. function autocompleteBox() {
  2. this.visible = false;
  3. this.timer = false;
  4. this.searchString = ''; // to keep track if the searchstring has changed (only then calling updateBox is useful)
  5. this.selectedIndex = -1; // index number of the manually selected result
  6. this.ajaxURL = '';
  7. this.ajaxQueryStringVariable = 'q';
  8.  
  9. this.$box = false; // quick reference
  10. this.$input = false; // quick reference
  11.  
  12. this.init = function(options) {
  13. this.$box = $('#'+options.boxId);
  14. this.$input = $('#'+options.inputId);
  15.  
  16. this.ajaxURL = options.ajaxURL;
  17.  
  18. if (options.ajaxQueryStringVariable) {
  19. this.ajaxQueryStringVariable = options.ajaxQueryStringVariable;
  20. }
  21.  
  22. this.hideBox();
  23.  
  24. // add event listener to inputfield
  25. var that = this;
  26. that.$input.keyup(function(e) {
  27. // apparently, we were still typing, so clear timer from the previous keystroke
  28. // this will prevent updateBox being called from the (any) previous keystroke
  29. clearTimeout(that.timer);
  30.  
  31. // special key actions, for cycling through the box
  32. // no need to return (it also seems to break jQuery?), as the search string is checked for changes
  33. // only useful if there are any results in the box (if there is anything to cycle through)
  34. if ($.inArray(e.keyCode, ['38', '40', '13']) && that.$box.find('a').length > 0) {
  35. that.cycleBox(e.keyCode);
  36. }
  37.  
  38. // only call updateBox when the search string changed and the string has a minimal of 2 chars
  39. if (that.searchString != that.$input.val() && that.$input.val().length > 1) {
  40. that.timer = setTimeout(function() {
  41. that.updateBox();
  42. }, 400); // you might want to play around with this timeout, 400ms seems a decent delay
  43. }
  44. });
  45. }; // init
  46.  
  47. // load results into the result box
  48. this.updateBox = function() {
  49. this.searchString = this.$input.val(); // update search string
  50.  
  51. var that = this;
  52. var data = {};
  53. data[that.ajaxQueryStringVariable] = that.$input.val(); // to avoid having to use eval() for dynamic variable names
  54. $.ajax({
  55. 'url': that.ajaxURL,
  56. 'data': data,
  57. 'dataType': 'json',
  58. 'success': function(data) {
  59. if (data.length) {
  60. var html = '';
  61. for (i in data) {
  62. // @todo make this snippet dynamic (option? template of some kind?)
  63. html += '<div class="autocomplete-box-row">\
  64. <a href="javascript:void(0)">'+data[i]+'</a>\
  65. </div>';
  66. }
  67.  
  68. // add HTML to box and thus to DOM
  69. that.$box.html(html);
  70.  
  71. // add listeners (works after HTML is added to box, but not before?
  72. // I though you could do something funky with $(html), then add everything to DOM? guess not...)
  73. they = that.$box.find('a');
  74.  
  75. // this is for mouse navigation, we have a separate routine (cycleBox) for handling keyboard navigation
  76. they.click(function(e) {
  77. e.preventDefault();
  78. // update the search string with the selected link
  79. // not necessary to prevent extra AJAX calls, because mouseclicks (probably) don't trigger keyUp events
  80. that.searchString = $(this).text();
  81. // put the text of the clicked link in the input field and hide the box
  82. that.$input.val($(this).text());
  83. that.hideBox();
  84. });
  85.  
  86. // add hover effect to links, this is done to have a uniform "hover" behaviour for
  87. // both mouse and keyboard navigation
  88. they.hover(function() {
  89. // we switched to mouse navigation, so clear selected index
  90. that.selectedIndex = -1;
  91. they.removeClass('hover');
  92. $(this).addClass('hover');
  93. }, function() {
  94. $(this).removeClass('hover');
  95. });
  96.  
  97. if (!that.visible) {
  98. that.showBox();
  99. }
  100. } else {
  101. // no results (anymore), hide box
  102. that.hideBox();
  103. }
  104. } // success
  105. });
  106. }
  107.  
  108. // separate function for all the animation etc. for showing the resultbox
  109. this.showBox = function() {
  110. this.visible = true;
  111. this.$box.show(); // animation, slideDown works too
  112. } // showBox
  113.  
  114. // separate function for all the animation etc. for hiding the resultbox
  115. this.hideBox = function() {
  116. this.visible = false;
  117.  
  118. // cleanup
  119. this.$box.html(''); // clear inner HTML
  120. // do NOT clear the searchstring as this might trigger another AJAX call
  121. this.selectedIndex = -1; // clear selected index (if any)
  122.  
  123. this.$box.hide(); // animation, slideUp works too
  124. } // hideBox
  125.  
  126. // function for tabbing through the results with keyboard (we don't actually use tab though)
  127. this.cycleBox = function(keyCode) {
  128. $links = this.$box.find('a');
  129.  
  130. if (keyCode == 40) { // down
  131. // init?
  132. if (this.selectedIndex == -1) {
  133. this.selectedIndex = 0;
  134. } else {
  135. this.selectedIndex = (this.selectedIndex + 1) % $links.length;
  136. }
  137. $links.removeClass('hover');
  138. activeItem = $links[this.selectedIndex]; // not a jQuery object?
  139. $(activeItem).addClass('hover');
  140. }
  141.  
  142. if (keyCode == 38) { // up
  143. if (this.selectedIndex == -1 || this.selectedIndex == 0) {
  144. // init?
  145. this.selectedIndex = $links.length - 1;
  146. } else {
  147. this.selectedIndex = this.selectedIndex - 1;
  148. }
  149. $links.removeClass('hover');
  150. activeItem = $links[this.selectedIndex]; // not a jQuery object?
  151. $(activeItem).addClass('hover');
  152. }
  153.  
  154. if (keyCode == 13) { // enter
  155. // was anything selected?
  156. if (this.selectedIndex > -1) {
  157. activeItem = $links[this.selectedIndex]; // not a jQuery object?
  158. // update the search string with the selected item, this will prevent extra AJAX calls
  159. this.searchString = $(activeItem).text();
  160. // put the text of the active link in the input field and hide the box
  161. this.$input.val($(activeItem).text());
  162. this.hideBox();
  163. }
  164. }
  165. } // cycleBox
  166. }; // autocompleteBox


PHP file die database simuleert met enkele voorbeeldwoorden die je kunt uitproberen voor de autocomplete (jquery.dropdown.php):
  1. <?php
  2. $q = !empty($_GET['q']) ? $_GET['q'] : '';
  3.  
  4. $matches = array();
  5. if (strlen($q)) {
  6. $words = array(
  7. 'test',
  8. 'testtwee',
  9. 'test met spatie',
  10. 'blaat',
  11. 'whatever',
  12. 'iets anders',
  13. 'anders',
  14. );
  15.  
  16. // case insensitive substring match
  17. foreach ($words as $word) {
  18. // from the start of a word
  19. // if (strtolower(substr($word, 0, strlen($q))) == strtolower(urldecode($q))) {
  20. // within any part of the word
  21. if (stripos($word, urldecode($q)) !== false) {
  22. $matches[] = htmlspecialchars($word, ENT_QUOTES); // should be safe, but escape them anyway
  23. }
  24. }
  25. }
  26. echo json_encode($matches);
  27. ?>


Have fun .

2 antwoorden

Gesponsorde links
Offline Wijnand - 26/11/2013 14:53
Avatar van Wijnand Moderator Getest en het werkt volgens mij wel goed. Ik had nog nooit //ajax... gezien in een script tag, maar het schijnt nog te werken ook :-).
Offline Thomas - 26/11/2013 16:24
Avatar van Thomas Moderator Dit stelde Google zelf voor. Waarschijnlijk zodat je de jQuery maar één keer cached voor zowel http als https, zoals hier wordt uitgelegd (oud artikel, maar nog steeds actueel denk ik).
Gesponsorde links
Je moet ingelogd zijn om een reactie te kunnen posten.
Actieve forumberichten
© 2002-2024 Sitemasters.be - Regels - Laadtijd: 0.201s