これが解決策のフィドルです。
一番下のコード
http://jsfiddle.net/R68jf/5/
元の投稿された問題を解決する方法を私に気づかせてくれた Red October の Capt. Marco Ramius に感謝します。「One Ping Only」.... 以下で説明します。
このhttp://jsfiddle.net/R68jf/5/フィドル は実用的な解決策を提示し、他の問題の解決にも役立つ可能性がある ui-autocomplete ウィジェットの便利なカスタマイズを示しています。具体的には、このソリューションは次の要件を満たしています。
1) ユーザーがマウスまたはキーボードでオートコンプリート メニュー項目をナビゲートするたびに、'focus' コールバックがトリガーされ、この場合、追加情報とリンクされた画像が表示されます。これは通常のオートコンプリートの動作であり、API で公開され、文書化されています。
2) ユーザーがメニューから離れたりメニューから出たりするときはいつでも、「フォーカス」コールバックの効果は元に戻せるはずです。これはデフォルトの動作ではありません。現在、「フォーカス」の副作用を元に戻すための「ぼかし」API は提供されていません。したがって、「ゾンビ」の画像は残りますが、メニュー項目はフォーカスされなくなります。元の質問フィドルは、このデフォルトと関連する「ゾンビ」効果を示しています。それを見るには、「spam」と入力し、メニューを上下にスクロールします。スクロールすると、副作用が更新されます。メニューからマウスを離すと……ゾンビがとどまります!!
3) ユーザーがマウス クリックまたはフォーカスされたアイテム上でリターン キーを使用して選択を行った場合、そのアイテムのフォーカスに関連する副作用が持続する必要があります。ドキュメント化された API で公開されている「select」メソッドを使用することで、これは簡単です。
4) メニュー選択を行った後、ユーザーが検索入力に戻って検索を変更した場合、副作用は逆になります。これはデフォルトでは発生しませんが、代わりにゾンビが発生します!
これらの要件を達成するために、私は次の方法を採用することになりました。私たちの中の上級者にとっては、これらは初歩的なものに見えるかもしれませんが、他の有給者にとっては、これは ui-autocomplete カスタマイズの役立つデモンストレーションになるかもしれません:
1) オートコンプリートの _renderItem メソッドをオーバーライドして、メニューの表示をカスタマイズします。
2) _move メソッドをオーバーライドして、キーボードを使用してメニューから移動するときの動作を変更します。
3) 「赤い 10 月」にインスパイアされた「One Ping Only」コールバックを使用して、フォーカスの副作用を逆転させ、システムを元の準備完了状態に戻します。これは jQuery .one() メソッドを使用します。
ここのコードには多くのコメントが付けられていますが、いくつかの点について言及する価値があります。
1) 一部のイベント (メニューからアイテムを選択してからメニューを閉じるなど) は、自然な動作の一部として、メニューのブラー イベントとマウスアウト イベントを自動的にトリガーします。これは問題になる可能性があります。現在の例では、すべてのゾンビを殺すための「ぼかし」機能が必要でしたが、アイテムが選択されると、その同じぼかし機能によって、保持したい正当なデータが失われてしまうからです。ぐらぐら!
2) $('#mySelector').autocomplete({blah:blah, blah:blah}) を実行すると、「uiAutocomplete」と呼ばれる ui.autocompete() クラスのオブジェクトが作成され、そのオブジェクトが作成されます。 $('#mySelector') 要素内にデータとして保存されます。したがって、オートコンプリート クラスからデータ、オプション、メソッドなどにアクセスするには、$('#mySelector').data() を調べるだけで、すべてが揃っています!
$('#mySelector').data('uiAutocomplete') のようなものがエンチラーダ全体を取得することを意味するため、これはかなり気の利いたものであり、オートコンプリート関数をオーバーライドしたい場合は、 $( '#mySelector').data('uiAutocomplete').myFavoriteFunction = function (){... 気の利いたことをする..}.
プロトタイプやサブクラス化などについて心配する必要はありません。これを試す良い方法は、正確な関数を ui-autocomplete ソース コードからコピーし、それをインスタンスに貼り付けてから使用することです。元のソース (クラス) コードを変更していないため、何も壊すことはできません。単にインスタンスのコードを変更しているだけです。楽しいね?
3) $('#mySelector').autocomplete({ foo: function (event, ui) {.. do stuff here..} を実行すると、この方法で必要な関数をほとんど作成し、次のように呼び出すことができます。 this._trigger('foo', event) を実行します。
つまり、'close'、'open'、'focus'、'select' などの '公式' メソッドは単に '提案を提供する' だけであり、実装に特別な魔法を必要とせずに、必要に応じて独自の関数を作成できます。彼ら。あなたの特別な機能は、公式のもののすぐ隣に「一等市民」として座ることができ、誰もその違いを知りません. 唯一の違いは、「組み込み」関数がソース コードで呼び出され、関数を手動で呼び出す必要があることです。$('#mySelector').data('uiAutocomplete').myFunctionName() を介してアクセスできるため、コードのどこからでも呼び出すことができます。
4) さらに、「ui-menu」や「uiAutocompetemenuItem」などのオブジェクトは、それぞれのオブジェクト内にデータとして保存されます。通常、これらのオブジェクトは、ページが最初にロードされたときには存在しませんが、on() を使用して、後で作成されるオブジェクトにイベントをバインドできます。たとえば、これは今後作成されるすべての 'li.spam' オブジェクトに mouseenter イベントをバインドし、マウスオーバーでそれらを赤くし、さらにオブジェクトの .data() 辞書から値を取得して $('#mySelector に割り当てます。 ')。:
$('body').on( {
mouseenter: function() {
$(this).children('a').css("color", "red");
var currentItem = $(this).data('uiAutocompleteItem');
$('#mySelector').value(currentItem.someValue);
}
}, "li.spam");
エンチラーダ全体のコード (自己完結型です。コメントが多すぎる場合はお詫びします):
<!DOCTYPE html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.0/jquery-ui.min.js"></script>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1/themes/smoothness/jquery-ui.css" type="text/css" />
<title>fiddle</title>
<script>
(function ($) {
var quoteList = [
{ 'id': "1",
'label': "We're here to preserve democracy.",
'value': "We're here to preserve democracy, not practice it.",
'description': "Capt. Ramsey to Lt. Commander Hunter",
'title': "Gene Hackman as Capt. Ramsey, Denzel Washington as Lt. Cmdr. Hunter",
'stub' : "Crimson Tide",
'image': "http://2.bp.blogspot.com/_nY72TdvmuKk/S0aoHWh0LeI/AAAAAAAABjY/Yu4krTxxboM/s400/crimsontide.jpg"
},
{ 'id': "2",
'label': "Well, don't like [that cigar] too much.",
'value': "Capt. Ramsey: How do you like that cigar? Hunter: It's good, sir. Capt. Ramsey: It's your first? Hunter: [coughing] Yeah. Capt. Ramsey: Well, don't like it too much. They're more expensive than drugs. ",
'description': "Capt. Ramsey to Lt. Commander Hunter",
'title': "Gene Hackman as Capt. Ramsey, Denzel Washington as Lt. Cmdr. Hunter",
'stub' : "Crimson Tide",
'image': "http://www.vimbly.com/blog/wp-content/uploads/2012/11/26-e1352860345409.jpg"
},
{ 'id': "3",
'label': "I expect and demand your very best.",
'value': "I expect and demand your very best. Anything less, you should have joined the Air Force.",
'description': "Ramsey to the crew of the Crimson Tide",
'title': "Gene Hackman as Capt. Ramsey, Denzel Washington as Lt. Cmdr. Hunter",
'stub' : "Crimson Tide",
'image': "http://i2.cdnds.net/12/34/618x405/movies_tony_scott_films_8.jpg"
},
{ 'id': "4",
'label': "The Kirby Silver Surfer is the only true Silver Surfer.",
'value': "Everybody knows that the Kirby Silver Surfer is the only true Silver Surfer.",
'description': "Hunter to Petty Officer First Class Danny Rivetti (Danny Nucci)",
'title': "Gene Hackman as Capt. Ramsey, Denzel Washington as Lt. Cmdr. Hunter",
'stub' : "Crimson Tide",
'image': "http://www.empireonline.com/images/features/a-z/quentin-tarantino/4.jpg"
},
{ 'id': "5",
'label': "I don't trust air I can't see.",
'value': "I don't trust air I can't see.",
'description': "Ramsey to Hunter",
'title': "Gene Hackman as Capt. Ramsey, Denzel Washington as Lt. Cmdr. Hunter",
'stub' : "Crimson Tide",
'image': "http://images3.cinema.de/imedia/1853/2211853,JSg9qf_ICg9XI70_mnk+0YKnEwjDCNvEJ135zZiOOG51Em+3vDb9zfon3uv_jNxJfz3ogxTr3jHE26akqhRXcA==.jpg"
},
{ 'id': "6",
'label': "Mr Hunter. I've made a decision.",
'value': "Mr Hunter. I've made a decision. I'm Captain of this boat. NOW SHUT THE FUCK UP!",
'description': "Ramsey to Hunter",
'title': "Gene Hackman as Capt. Ramsey, Denzel Washington as Lt. Cmdr. Hunter",
'stub' : "Crimson Tide",
'image': "http://images.static-bluray.com/reviews/271_1.jpg"
},
{ 'id': "7",
'label': "Mr. Vossler, this is Captain Kirk!",
'value': "Mr. Vossler, this is Captain Kirk! I need warp speed on that communications unit!",
'description': "Hunter to Petty Officer Third Class Russell Vossler (Lillo Brancato)",
'title': "Gene Hackman as Capt. Ramsey, Denzel Washington as Lt. Cmdr. Hunter",
'stub' : "Crimson Tide",
'image': "http://www.livedash.com/thumb_v5/20091130/crimson%20tide-2009-11-30-0/2_crimson%20tide-2009-11-30-0.jpg"
},
{ 'id': "8",
'label': "I will marry a round American woman and raise rabbits.",
'value': "I will live in Montana. And I will marry a round American woman and raise rabbits, and she will cook them for me. And I will have a pickup truck... maybe even a recreational vehicle. And drive from state to state. Do they let you do that? ",
'description': "Capt. Vasili Borodin (Sam Neill) to Ramius",
'title': "Sean Connery as Capt. Ramius, Alec Baldwin as Jack Ryan",
'stub' : "Crimson Tide",
'image': "http://1.bp.blogspot.com/-08wMMkx53XQ/TrNoK0ykLzI/AAAAAAAAkK0/p5Q_F6EOrvI/s360/Crimson-Tide.jpg"
},
{ 'id': "9",
'label': "We will lay off their largest city, and listen to their rock and roll!",
'value': "We will pass through the American patrols, past their sonar nets, and lay off their largest city, and listen to their rock and roll... while we conduct missile drills.",
'description': "Ramius to the crew of the The Hunt for Red October",
'title': "Sean Connery as Capt. Ramius, Alec Baldwin as Jack Ryan",
'stub' : "The Hunt for Red October",
'image': "http://libcom.org/files/images/library/alec_baldwin1%5B1%5D.jpg"
},
{ 'id': "10",
'label': "You're afraid. Well, you should be. Personally, I'd give us one chance in three. More tea anyone?",
'value': "You're afraid of our fleet. Well, you should be. Personally, I'd give us one chance in three. More tea anyone? ",
'description': "Ramius to the officers of the The Hunt for Red October",
'title': "Sean Connery as Capt. Ramius, Alec Baldwin as Jack Ryan",
'stub' : "The Hunt for Red October",
'image': "http://i77.photobucket.com/albums/j60/bluinkalchemist/hunt-for-red-october_480_poster.jpg"
},
{ 'id': "11",
'label': "Hey, Ryan, be careful what you shoot at. Most things in here don't react too well to bullets.",
'value': "Hey, Ryan, be careful what you shoot at. Most things in here don't react too well to bullets.",
'description': "Ramius to Ryan",
'title': "Sean Connery as Capt. Ramius, Alec Baldwin as Jack Ryan",
'stub' : "The Hunt for Red October",
'image': "http://wodumedia.com/wp-content/uploads/2012/10/Sean-Connery-and-Alec-Baldwin-in-Paramounts-The-Hunt-For-Red-October-1990-0.jpg"
},
{ 'id': "12",
'label': "When he reached the New World, Cortez burned his ships. As a result his men were well motivated.",
'value': "When he reached the New World, Cortez burned his ships. As a result his men were well motivated.",
'description': "Remius to the officers of the The Hunt for Red October",
'title': "Sean Connery as Capt. Ramius, Alec Baldwin as Jack Ryan",
'stub' : "The Hunt for Red October",
'image': "http://content.internetvideoarchive.com/content/photos/070/002974_25.jpg"
},
{ 'id': "13",
'label': "Russians don't take a dump, son, without a plan.",
'value': "'The average Ruskie, son, don't take a dump without a plan.' Wait a minute. We don't have to figure out how to get the crew off the sub. He's already done that, he would have had to. All we gotta do is figure out what he's gonna do. So how's he gonna get the crew of the sub. ",
'description': "Ryan reliving a comment by Admiral Josh Painter (Fred Thompson)",
'title': "Sean Connery as Capt. Ramius, Alec Baldwin as Jack Ryan",
'stub' : "The Hunt for Red October",
'image': "http://toddmpost.files.wordpress.com/2010/12/admiralpainter.jpg?w=640"
},
{ 'id': "14",
'label': "At that speed, they could run right over my daughter's stereo and not hear it.",
'value': "They're pinging away with their active sonar like they're looking for something, but nobody's listening... they're moving at almost forty knots. At that speed, they could run right over my daughter's stereo and not hear it.",
'description': "Capt. Davenport (Daniel Davis) to Ryan",
'title': "Sean Connery as Capt. Ramius, Alec Baldwin as Jack Ryan",
'stub' : "The Hunt for Red October",
'image': "http://libcom.org/files/images/library/alec_baldwin1%5B1%5D.jpg"
},
{ 'id': "15",
'label': "I would like to have seen Montana.",
'value': "I would like to have seen Montana.",
'description': "Capt. Vasili Borodin (Sam Neill) to Capt. Ramius",
'title': "Sean Connery as Capt. Ramius, Alec Baldwin as Jack Ryan",
'stub' : "The Hunt for Red October",
'image': "http://img.geocaching.com/track/display/ad4047d5-a1f9-4a39-86c9-8a7a0873f1ef.jpg"
},
{ 'id': "16",
'label': "Next time, Jack, write a goddamn memo.",
'value': "Next time, Jack, write a goddamn memo.",
'description': "Ryan (talking to himself)",
'title': "Sean Connery as Capt. Ramius, Alec Baldwin as Jack Ryan",
'stub' : "The Hunt for Red October",
'image': "http://www.wearysloth.com/Gallery/ActorsB/860-9042.gif"
},
{ 'id': "17",
'label': "Give me a ping, Vasili. One ping only, please.",
'value': "Re-verify our range to target... one ping only. Give me a ping, Vasili. One ping only, please.",
'description': "Ramius to Capt. Vasili Borodin (Sam Neill)",
'title': "Sean Connery as Capt. Ramius, Alec Baldwin as Jack Ryan",
'stub' : "The Hunt for Red October",
'image': "http://25.media.tumblr.com/tumblr_ma1prrCDn81qkcj94o1_1280.jpg"
},
{ 'id': "18",
'label': "Come up right behind his propellor and he'll be deaf as a post!",
'value': "Not if we stay in his baffles, Seaman Beaumont. Not if we stay in his baffles. Come up right behind his propellor and he'll be deaf as a post!",
'description': "Seaman 'Jonesy' Jones (Courtney Vance) to Seaman Beaumont (Ned Vaughn)",
'title': "Sean Connery as Capt. Ramius, Alec Baldwin as Jack Ryan",
'stub' : "The Hunt for Red October",
'image': "http://cdn3.hark.com/images/000/375/672/375672/original.jpg"
},
{ 'id': "19",
'label': "Andrei, you've lost another submarine?",
'value': "One of our submarines, an Alfa, was last reported in the area of the Grand Banks. We have not heard from her for some time..... Andrei, you've lost another submarine?",
'description': "Dr. Jeffrey Pelt (Richard Jordan) to Russian Ambassador Andrei Lysenko (Joss Ackland)",
'title': "Sean Connery as Capt. Ramius, Alec Baldwin as Jack Ryan",
'stub' : "The Hunt for Red October",
'image': "http://www.wearysloth.com/Gallery/ActorsJ/8969-9042.gif"
}
];
$(document).ready (function () {
$( "#id_quote" ).autocomplete({
minLength: 2,
delay: 2,
source: quoteList,
open: function(event, ui) {
// defines the character of the ui-menu
$(".ui-autocomplete")
.css("width", "300px")
.css("max-height", "200px")
.css("overflow-y", "scroll")
},
focus: function( event, ui ) {
// This causes ancillary fields to populate whenever a menu choice is hovered
// also works if menu choices are navigated with keyboard up and down arrows
$( "#id_stub" ).val( ui.item.stub );
$( "#id_title" ).val( ui.item.title );
$( "#id_description" ).val( ui.item.description );
$( "#id_dialog" ).val( ui.item.value );
$( '#id_image').html("<img style='width:100%; height:100%;' src='" + ui.item.image + "' />");
// Ensures that when mouse leaves the manu, that the ancillary fields are emptied
// This is a 'one ping only' event, because 'mouseout' is triggered by the closing of the
// menu when a true choice is made (either by mouse click or return key).
// Making this 'one ping only' ensures that the ancillary fields don't empty
// when the menu closes after a valid selection is made.
// Of course, if there is a new focus, the 'one ping only' rule must be re-established,
// so it is put here, in the focus callback:
$('li.one-ping-only').one ("mouseleave", function() {
$( "#id_stub" ).val("");
$( "#id_title" ).val("");
$( "#id_description" ).val("");
$( "#id_dialog" ).val("");
$( '#id_image').empty();
});
},
select: function( event, ui ) {
// Takes off the 'one ping only' mouse event binding,
// otherwise it would cause the ancillary fields to empty
// due to the 'mouseout' that happens automatically when
// the menu closes.
// In some cases, the mouseout binding has already been removed, but
// in other cases not (by a true mouseout event). This is a safe
// way to make sure before a selection event fills the fields.
$('li.one-ping-only').off("mouseleave");
$( "#id_stub" ).val( ui.item.stub );
$( "#id_title" ).val( ui.item.title );
$( "#id_description" ).val( ui.item.description );
$( "#id_dialog" ).val( ui.item.value );
$( '#id_image').html("<img style='width:100%; height:100%;' src='" + ui.item.image + "' />");
$( "#id_quote" ).val( ui.item.value )
// In general, we want to make sure that if the user goes back and types
// again or backspaces in the autocomplete input, AFTER making a selection, then the
// ancillary fields will blank, to avoid zombie data persistence.
// Unfortunately, making a selection with the return key (vs. a mouse click)
// sends (or propagates?) a return key event to the original input field, AFTER the select
// event has been called. Thus, though a valid select event populates the fields (by the
// actions immediately above), the ancillary data is them immediately erased
// by this key event that arrives a split second later.
// To counter this, we make an exception so that in the event that the first key event arriving
// after a select event happens to be a return key, (the selection was made with the keyboard)
// then that single key event is exempted from blanking the field.
.one ("keypress", function (e) {
if (e.keyCode !== 13) {
$( "#id_stub" ).val("");
$( "#id_title" ).val("");
$( "#id_description" ).val("");
$( "#id_dialog" ).val("");
$( '#id_image').empty();
}
else {
// The 'one ping only' event above is REMOVED if any key event takes place, even if it was
// as 'disqualified' return key event. So, even though nothing happened visibly,
// we need to re-establish our 'one ping only' protection from 'real editing' key event.
$( "#id_quote" ).one ("keypress", function (e) {
if (e.keyCode !== 13) {
$( "#id_stub" ).val("");
$( "#id_title" ).val("");
$( "#id_description" ).val("");
$( "#id_dialog" ).val("");
$( '#id_image').empty();
}
})
}
});
}
}).data("uiAutocomplete")._renderItem = function( ul, item ) {
// The uiAutocomplete object is stored as part data in the input field it is attached to.
// When we call .data("uiAutocomplete") here, it relates back to data attribute of the $( "#id_quote" ) that the
// autocomplete widget function was attached to. It's helpful to realize that the uiAutocomplete object is part of the
// $( "#id_quote" ).data() and can be called at any time, not just here right after autocomplete is called.
// This can be confusing, but just remember that once you do $( "#id_quote" ).autocomplete({blah blah}), then forever
// .data() attribute of the $( "#id_quote" ) will contain a 'uiAutocomplete' object, the properties of which are the
// properties defined in the jQuery-ui class autocomplete, as instantiated by you.
// Anyway, we want to override one of the autocomplete methods, specifically the _renderItem method
// that makes the items in the menu.
// Here, we specify the nature of the presentation, giving it first the traditional label, and also another line
// that in this case is item.description.
// In addition, we perform a regular expression match on the label, and highlight the letters/words
// that match the search term we have typed into our search input.
var regxpmatch = new RegExp(this.term, "ig");
// Note the 'g' in 'ig' means global to catch any place the letters match, otherwise the default is to match only the first
// instance in any item.label.
// Also note the 'i' in 'ig' makes the match case insensitive.
var formatted_quote = item.label.replace(regxpmatch,"<span style='font-weight:bold;color: red;'>" + this.term + "</span>");
return $( "<li class='one-ping-only'></li>" )
// the 'one-ping-only' class is the hook for our callbacks that erase ancillary fields when a user edits the original
// input after making a selection, and also is used for another callback that erases ancillary fields if the menu loses
// focus by moving the mouse out of the menu, or alternatively using the up or down key on the keyboard to leave
// the menu.
.append( "<a><div class='subquote'>" + formatted_quote + "</div><div class='subtitle'>" + item.description + "</div></a><hr>" )
// makes the menu item have two lines, and puts a horizontal line between each item pair.
.appendTo( ul );
};
$( "#id_quote" ).data( "uiAutocomplete" )
._move = function( direction, event ) {
// here we are using a similar logic to override the _move method, ensuring that if the user exits the menu
// using the keyboard, the ancillary fields will clear, just as they do with the 'one ping only' mouseout function
// described above.
if ( !this.menu.element.is( ":visible" ) ) {
this.search( null, event );
return;
}
if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
this.menu.isLastItem() && /^next/.test( direction ) ) {
this._value( this.term );
$( "#id_stub" ).val("");
$( "#id_title" ).val("");
$( "#id_description" ).val("");
$( "#id_dialog" ).val("");
return;
}
this.menu[ direction ]( event );
};
});
})(jQuery);
</script>
<style type="text/css">
div.subquote {font-size: 11px;}
div.subtitle {font-size: 9px; color: blue;}
#id_quote {width: 250px; height:100px;}
#id_stub {width: 250px;}
#id_title, #id_description {width: 250px;}
#id_dialog {width: 250px; min-height: 100px;}
</style>
</head>
<body>
<form action="#" method='POST'>
<div style="float:left; width: 300px;">
<h4>Search submarine movie quotes...</h4>
<h4>...or simply write in your own!</h4>
<div>
<textarea name="quote" type="text" id="id_quote" placeholder="Start typing here...." ></textarea>
</div>
</div>
<div style="float:left; margin-left: 50px;">
<div style="margin: 5px 0 5px 10px;">
<input name="stub" type="text" id="id_stub" />
</div>
<div style="margin: 5px 0 5px 10px;">
<textarea name="title" type="text" id="id_title" ></textarea>
</div>
<div style="margin: 5px 0 5px 10px;">
<textarea name="description" type="text" id="id_description"></textarea>
</div>
<div style="margin: 5px 0 5px 10px;">
<textarea name="dialog" type="text" id="id_dialog"></textarea>
</div>
<button type="submit" id="id_button">Submit your Favorite!</button>
<div name="image" id="id_image" style="width: 250px; height:250px;">
</div>
</div>
</form>
</body>