I've just recently been studying JQuery to use on a personal website. Something I wanted to add to the website was a blog preview feature, which uses AJAX and JSON to retrieve the title and preview text of a blog post. When a visitor clicks the blog tab, JQuery retrieves the information and is displaying the titles the way I want it to. The titles are supposed to be clickable, so that when you click a title the preview text is shown. For the most part I have this working by using JQuery's .on() function, however for whatever reason only every other title is clickable. Here is the code:
$(document).ready(function() {
function handleSelect(event, tab) {
if (tab.index == 1) {
$("#blogContent").empty();
$.getJSON("/TimWeb/blogPreview", function(data) {
$.each(data, function(i) {
$("#blogContent").append("<h3 class=head>" +
data[i].blogTitle + "</h3>" +
"<p>" + data[i].blogBody + "</p>");
$("#blogContent .head").on("click", function() {
$(this).next().toggle();
}).next().hide();
});
});
}
}
var tabOpts = {
select:handleSelect
};
$(".tabs").tabs(tabOpts);
});
For a more visual description of the problem, if I have eight blog posts that are being previewed, the title for each will be rendered appropriately, with the content hidden. If I try clicking the first, third, fifth, or seventh title, nothing happens. If I click the second, fourth, sixth, or eighth titles, the post preview will appear. If I click it again, it will be hidden, as I expect it to be.
In case it causes any confusion, blogContent is the id of the div referenced by the jQuery tab for the blog section. I would greatly appreciate any advice or wisdom you could lend me!
You don't need to attach the event to each individual h3.
.on() can be used to attach a function to an event for everything, both now and in the future, that match a selector (jQuery 1.7+).
Try taking the .on() out of the each loop (and the function), hide the p tag via style="display:none;" and place this after the function:
$(document).on("click", "#blogContent .head", function(){ $(this).next().toggle(); });
Something like this:
function handleSelect(event, tab) {
if (tab.index == 1) {
$("#blogContent").empty();
$.getJSON("/TimWeb/blogPreview", function(data) {
$.each(data, function(i) {
$("#blogContent").append("<h3 class=head>" +
data[i].blogTitle + "</h3>" +
"<p style='display:none;'>" + data[i].blogBody + "</p>");
});
});
}
}
// This only needs to be executed once.
$(document).on("click", "#blogContent .head", function(){ $(this).next().toggle(); });
I would suggest moving your on statement outside of the each statement. Per jQuery:
If new HTML is being injected into the page, select the elements and
attach event handlers after the new HTML is placed into the page. Or,
use delegated events to attach an event handler, as described next.
http://api.jquery.com/on/
So something like this:
function handleSelect(event, tab) {
if (tab.index == 1) {
$("#blogContent").empty();
$.getJSON("/TimWeb/blogPreview", function(data) {
$.each(data, function(i) {
$("#blogContent").append("<h3 class=head>" +
data[i].blogTitle + "</h3>" +
"<p>" + data[i].blogBody + "</p>");
$("#blogContent .head").next().hide();
});
$("#blogContent .head").on("click", function() {
$(this).next().toggle();
});
});
}
}
If this is something that happens multiple times you would be better served using the delegated approach outlined by Jay and setting the event on they body outside of all functions (excepting document.ready).
Related
I'm trying to stop jQuery Mobile hiding the loading spinner when changePage is called.
The program flow goes like this, starting with clicking a link, which has its click event defined like this:
$('body').delegate('.library-link', 'click', function() {
$.mobile.loading( 'show' );
$.mobile.changePage($('#page-library'));
return false;
});
Upon clicking the link, the pagebeforeshow event is fired, which triggers a function to populate the page from the local storage, or else make an ajax call to get the data.
$(document).on('pagebeforeshow', '#page-library', function(event){
ui.populate_data();
});
In ui.populate_data() we get the data from local storage or make an ajax call.
ui.populate_data = function() {
if (localdata) {
// populate some ui on the page
$.mobile.loading( 'hide' );
} else {
// make an ajax call
}
};
If the data is there, we load the data into the container and hide the loading spinner. If not it makes the ajax call, which on complete saves the data in local storage, and calls ui.populate_data()
The problem is, after the pagebeforeshow event is finished, changePage is calling $.mobile.loading( 'hide' ), even though the data might not be there yet. I can't find any way to prevent changePage from hiding the spinner, other than by temporarily redefining $.mobile.loading, which feels pretty wrong:
$('body').delegate('.library-link', 'click', function() {
$.mobile.loading( 'show' );
loading_fn = $.mobile.loading;
$.mobile.loading = function() { return; };
$.mobile.changePage($('#page-library'), {showLoadMsg: false});
return false;
});
and before hiding the spinner in my ui function:
ui.populate_data = function() {
if (localdata) {
// populate some ui on the page
if (typeof loading_fn === 'function') {
$.mobile.loading = loading_fn;
}
$.mobile.loading( 'hide' );
} else {
// make an ajax call
}
};
Surely there must be a way to get complete control over the showing and hiding of the loading widget, but I can't find it. I tried passing {showLoadMsg: false} to changePage, but as suggested by the docs it only does things when loading pages over ajax, which I'm not doing.
Maybe it's too much for many, but I found a solution other than the written in the comments (which didn't work for me).
I use the jquery mobile router and in the 'show' event of a page, I do $.mobile.loading("show");, so when the page appears it does with the loading spinner showing.
Though to hide the spinner, I had to use $('.ui-loader').hide();, which is weird, I know...
I use Jquery Mobile Router for a lot more, but it solved this issue.
(Maybe just listening to the proper event and triggering the spinner would also work, as this is what JQMR does...)
I'm using JQM 1.4.2...
In jqm I have something like this
<li>cats</li>
<li>dogs</li>
Page1 is a dynamic page and should load it's contents based on which link button I pressed and I use
$(document).on('pagebeforeshow', '#page1', function(event, data){
do something here with either cats or dogs
});
Now how do I determine here what link what used?
I could use sessionStorage or perhaps a
$('.loadPage').click(function(){
$(this).attr('id').blablabla
})
but it doesn't seem right to me. I'm sure the answer is there right in front of me, but I can't seem to get around it.
Any ideas?
You could store the id in #page1's data attribute on click of loadPage:
$(document).on("pageinit", "#page0", function () {
$(this).on("click", ".loadPage", function (e) {
e.preventDefault();
$("#page1").data("id", this.id);
});
});
Then, after jQM redirects you to #page1, check for the stored data :
$(document).on("pagebeforeshow", "#page1", function () {
alert($(this).data("id"));
// you can do anything with this id
});
Demo : http://jsfiddle.net/hungerpain/wN2L9/
compare the id:
if(this.id==='cats')
{
//do something here
}
On a side note you can use $(this).attr('id') also, but its better you use pure JavaScript whenever possible.
I have a list of links, and I have this search box #reportname. When the user types in the search box, autocomplete will show the text of the links in a list.
<div class="inline">
<div class="span-10">
<label for="reportname">Report Name</label>
<input type="text" name="reportname" id="reportname" />
</div>
<div class="span-10 last">
<button type="button" id="reportfind">Select</button>
</div>
</div>
The user can then use the keyboard arrow to select one of the text, and when he press ENTER, browser will go to the address of the link. So far so good.
<script type="text/javascript">
$(document).ready(function () {
$("#reportname").autocomplete({
source: $.map($("a.large"), function (a) { return a.text }),
select: function () { $("#reportfind").click() }
})
$("#reportfind").click(function () {
var reportname = $("#reportname")[0].value
var thelinks = $('a.large:contains("' + reportname + '")').filter(
function (i) { return (this.text === reportname) })
window.location = thelinks[0].href
})
});
</script>
The issue is when the user types, autocomplete shows a list, and then the user use the mouse to click one of the result. With keyboard navigation, the content of the search box is changed, but if the user clicks one of the options, the search box is not modified and the select event is immediately triggered.
How can I make the script work with keyboard selection and mouse selection? How can I differentiate between select events that are triggered by keyboard with the ones triggered by mouse?
To your 2nd question: "How can I differentiate between select events that are triggered by keyboard with the ones triggered by mouse?"
The event object in the jQuery UI events would include a .originalEvent, the original event it wrapped. It could have been wrapped multiple times though, such as in the case of Autocomplete widget. So, you need to trace up the tree to get the original event object, then you can check for the event type:
$("#reportname").autocomplete({
select: function(event, ui) {
var origEvent = event;
while (origEvent.originalEvent !== undefined)
origEvent = origEvent.originalEvent;
if (origEvent.type == 'keydown')
$("#reportfind").click();
},
...
});
Thanks to #William Niu and firebug, I found that the select event parameter 'ui' contains the complete selected value: ui.item.value. So instead of depending on jquery UI to change the text of the textbox, which didn't happen if the user clicks with mouse, I just pick up the selected value from 'ui':
$("#reportname").autocomplete({
select: function (event, ui) {
var reportname = ui.item.value
var thelinks = $('a.large:contains("' + reportname + '")').filter(
function (i) { return (this.text === reportname) })
window.location = thelinks[0].href
};
})
I tested it in all version of IE (inlcuding 9) and always ended up with an empty input-control after I selected the item using the mouse. This caused some headaches. I even went down to the source code of jQuery UI to see what happens there but didn’t find any hints either.
We can do this by setting a timeout, which internally queues an event in the javascript-engine of IE. Because it is guaranteed, that this timeout-event will be queued after the focus event (this has already been triggered before by IE itself).
select: function (event, ui) {
var label = ui.item.label;
var value = ui.item.value;
$this = $(this);
setTimeout(function () {
$('#txtBoxRole').val(value);
}, 1);
},
Had the same issue / problem.
Jquery: 1.11.1
UI: 1.11.0
Question: Do you use bassistance jquery validte plugin simultanously?
If positive: update this to a newest version or just disable it for tests.
I updated from 1.5.5 to 1.13.0
Helped for me. Good luck!
I recently encountered the exact same problem (autocomplete items not clickable, keyboard events working).
Turned out that in my case the answer was not at all JS related. The autocomplete UI was not clickable simply because it was lacking an appropriate value for the z-index CSS property.
.ui-autocomplete {
z-index: 99999; /* adjust this value */
}
That did the trick.
This may be a bit farshot, but I had a similar situation where selecting an autocomplete value left the input field empty. The answer was to ignore the "change" events (as those were handled by default) and replace them with binds to "autocompletechange" events.
The "change" event gets triggered before the value from autocomplete is in the field => the field had "empty" value when handling the normal "change" event.
// ignore the "change" event for the field
var item = $("#"+id); // JQuery for getting the element
item.bind("autocompletechange", function(event, ui) { [call your handler function here] }
I was facing a similar problem. I wanted to submit the form when the user clicked on an option. But the form got submitted even before the value of the input could be set. Hence on the server side the controller got a null value.
I solved it using a modified version of William Niu's answer.
Check this post - https://stackoverflow.com/a/19781850/1565521
I had the same issue, mouse click was not selecting the item which was clicked.My code was supposed to make an ajax call to fetch the data as per the selection item from autocomplete source.
Previous code: mouse click not working.
select: function(event, ui) {
event.preventDefault();
for(i= 0; i< customer.length; i++)
if(document.getElementById('inputBox').value == customer[i].name)
{
$.ajax({
call
})
Changed code :mouse click working
select: function(event, ui) {
// event.preventDefault();
for(i= 0; i< customer.length; i++)
// if(document.getElementById('inputBox').value == customer[i].fields.name)
if(ui.item.value == customer[i].name)
{
$.ajax({
call
})
After inspecting the code in the developer tools console, I noticed there were two list items added. I removed the pairing <li></li> from my response code and oh yeah, the links worked
I also added this function as the click event:
$("#main-search").result(function ()
{
$("#main-search").val("redirecting...."), window.location.href = $("#main-search").attr("href").match(/page=([0-9]+)/)[1];
})
This works and you can test it here: Search for the term dress -->
example: i have an un-ordered list containing a bunch of form inputs.
after making the ul .sortable(), I call .disableSelection() on the sortable (ul) to prevent text-selection when dragging an li item.
..all fine but I need to re/enable text-selection on the form inputs.. or the form is basically un-editable ..
i found a partial solution # http://forum.jquery.com/topic/jquery-ui-sortable-disableselection-firefox-issue-with-inputs
enableSelection, disableSelection seem still to be un-documented: http://wiki.jqueryui.com/Core
any thoughts?
solved . bit of hack but works! .. any comments how i can do this better?
apply .sortable() and then enable text-selection on input fields :
$("#list").sortable({
stop: function () {
// enable text select on inputs
$("#list").find("input")
.bind('mousedown.ui-disableSelection selectstart.ui-disableSelection', function(e) {
e.stopImmediatePropagation();
});
}
}).disableSelection();
// enable text select on inputs
$("#list").find("input")
.bind('mousedown.ui-disableSelection selectstart.ui-disableSelection', function(e) {
e.stopImmediatePropagation();
});
A little improvement from post of Zack - jQuery Plugin
$.fn.extend({
preventDisableSelection: function(){
return this.each(function(i) {
$(this).bind('mousedown.ui-disableSelection selectstart.ui-disableSelection', function(e) {
e.stopImmediatePropagation();
});
});
}
});
And full solution is:
$("#list").sortable({
stop: function () {
// enable text select on inputs
$("#list").find("input").preventDisableSelection();
}
}).disableSelection();
// enable text select on inputs
$("#list").find("input").preventDisableSelection();
jQuery UI 1.9
$("#list").sortable();
$("#list selector").bind('click.sortable mousedown.sortable',function(e){
e.stopImmediatePropagation();
});
selector = input, table, li....
I had the same problem. Solution is quite simple:
$("#list").sortable().disableSelection();
$("#list").find("input").enableSelect();
The following will disable selection for the entire document, but input and select elements will still be functional...
function disableSelection(o) {
var $o = $(o);
if ($o.find('input,select').length) {
$o.children(':not(input,select)').each(function(x,e) {disableSelection(e);});
} else {
$o.disableSelection();
}
}
disableSelection(document);
But note that .disableSelection has been deprecated by jquery-ui and will someday go away.
EASY! just do:
$( "#sortable_container_id input").click(function() { $(this).focus(); });
and replace "sortable_container_id" with the id of the element that is the container of all "sortable" elements.
Quite old, but here is another way:
$('#my-sortable-component').sortable({
// ...
// Add all non draggable parts by class name or id, like search input texts and google maps for example
cancel: '#my-input-text, div.map',
//...
}).disableSelection();
This post talks about the problem that jQuery tabs is still having with the back button. What the customer needs is fairly simple - when they press back, they want to go back to what they were just looking at. But jQuery tabs loads the first tab, not the one that was selected when the user left the page.
I've read the accompanying link and it says "It is planned and Klaus is working on it whenever he finds the time."
Has anyone solved the back button problem with jQuery UI tabs?
Using the solution to the history problem easement posted, a dirty fix for the back button problem would be to periodically check the location.hash to see if it has changed, and if it has, fire the click event of the appropriate link.
For example, using the zachstronaut.com updated jQuery Tabs Demo, you could add this to the $(document).ready callback, and it would effectively enable the back button to switch tabs, with no more than a 500ms delay:
j_last_known_hash = location.hash;
window.setInterval(function() {
if(j_last_known_hash != location.hash) {
$('#tabs ul li a[href="'+ location.hash +'"]').click();
j_last_known_hash = location.hash;
}
}, 500);
Have you tired updating the browsers location as you switch tabs?
http://www.zachstronaut.com/posts/2009/06/08/jquery-ui-tabs-fix.html
if you had a class on your tab container that was tabContainer, to update the url when user clicks a tab, you could do this:
$(".tabContainer").tabs({
select: function(event, ui) {
window.location.hash = ui.tab.hash;
}
});
then, instead of firing click, you could use the tabs select method if you have some getIndexForHash method that can return the right tab number for the selected hash value:
var j_last_known_hash = location.hash;
window.setInterval(function() {
var newHash = location.hash;
if(j_last_known_hash != newHash) {
var index = getIndexForHash(newHash);
$('.tabContainer').tabs("select",index);
j_last_known_hash = newHash;
}
}, 100);
window.onhashchange = function () {
const $index = $('a[href="' + location.hash + '"]').parent().index();
$('.tabContainer').tabs('option', 'active', $index);
}