I have an application containing a form with five input fields. When the user clicks on an input field a tooltip should be displayed, which works fine. The problem comes when I try to remove the tooltip, which happens if the user clicks on another input field. Below is a piece of code from my application, and I hope it's enough to understand how it works.
The arguments that are passed to "showTooltip()" are a DOM reference to the clicked input field, the text that is displayed in the tooltip, and a number (0-4) that is used to find the containing div that's surrounding the clicked input field (all input fields are inside their own div).
The application works fine, but after I've clicked around a couple of times on the fields the following message is displayed in the console: "Node was not found - inputDiv.removeChild(tooltip). I've found out that the reason for this is that the function "hideTooltip()" is sometimes called twice, but I can't find out the reason why this happens.
Any clues?
showTooltip: function(inputField, tooltipText, divNr){
var container = document.getElementById('container');
var inputDiv = container.getElementsByTagName('div');
var inputDiv = inputDiv[divNr];
var tooltip = document.createElement('div');
tooltip.className = "tooltip";
var text = document.createTextNode(tooltipText);
tooltip.appendChild(text);
inputDiv.appendChild(tooltip);
inputField.addEventListener('blur', function() { hideTooltip(inputField, inputDiv, tooltip, inputNode);});
},
hideTooltip: function(inputField, inputDiv, tooltip, nr){
inputDiv.removeChild(tooltip);
validateField(inputField);
}
unbind is jquery function, so it will not work unless you are using jquery. You will need to use the removeEventListener function without jquery. If you do decide to use jquery, I would recommend the one (http://api.jquery.com/one/) function that executes an event only once per element. I am not sure if you will need to check to see if the event exists before you call removeEventListener.
var blurEvent = function() { hideTooltip(inputField, inputDiv, tooltip, inputNode); };
inputField.removeEventListener('blur', blurEvent);
inputField.addEventListener('blur', blurEvent);
Every time an input field is clicked you're binding the 'blur' event to that field. Click again another event is bound to that field. This is why it's getting called more than once because you're binding everytime. You can unbind first, then add again. There might be a better solution, but this will work:
inputField.unbind('blur');
inputField.addEventListener('blur', function() { hideTooltip(inputField, inputDiv, tooltip, inputNode);});
Note: This will remove ALL event functions bound to 'blur' on that element
In your ShowTooltip function, you are adding an Event Listener that triggers hideTooltip on blur. If you show the tooltip more than once, you are setting more than one onBlur event listener, which means that next time you blur, you will trigger the hideTooltip function twice.
One solution is to unbind the event listener from the inputField when hideTooltip is run.
Good luck.
Related
How do you selectize.addItem("value") without triggering item_add?
I have a tags field that’s dependent on another selection. I need to automatically fill in tags whenever that other selection is changed, and then I need to have a listener run code whenever the user adds or removes a tag. I can’t figure out how to add the tags programmatically without triggering the item_add event, but I don’t want it triggering before the user’s even touched the tags.
(There is addItem(…, silent), but unless I’m mistaken, that only stops it from triggering the change event.)
Sample code:
$('#input-tags').selectize({
onItemAdd: function() {
alert("This should only appear by user action");
}
});
$tags = $('#input-tags')[0].selectize;
$tags.addItem("awesome");
$tags.addItem("neat");
JSFiddle
One solution (and the only one I know of) is to remove the event listeners before the addItems and add them back afterwards. That hardly seems ideal, though, especially if it needs to be done more than once.
You can convert your code to register the add callback after adding all initial values:
$('#input-tags').selectize();
$tags = $('#input-tags')[0].selectize;
$tags.addItem("awesome");
$tags.addItem("neat");
// Register the listener once you have initialized the selectize component.
$tags.on('item_add', function() {
alert("This should only appear by user action");
});
I'm facing this weird issue with off() and on() event binding to a select dropdown:
If I unbind and then rebind the change event to the select dropdown I won't be able to change the dropdown shown value. In other words, the selected value is not updated properly in the dropdown, even if the change event is triggered.
If I remove the off() part, leaving only the event bounding with on(), everything works fine but obviously I'm not able to prevent the binding of the same event more than once.
See a live example here http://jsfiddle.net/z7o11exs/
Test case:
use the dropdown (it works! the selected value is correctly show in the dropdown)
refresh page. click on the first button (off/on) and then use the dropdown. It does not work properly as the selected value does not change
refresh page. click on the second button (only on) and then use the dropdown. It does work as expected. side effect: clicking n times on the 2nd button bounds n times the change event to the dropdown element
Here's the code:
//--- This binds the event to the element
function bindEvent(){
$("#myselect").on("change", function(){
console.log("change");
});
}
//--- remove any change event previously added, then rebind it
function rebindEvent(){
$("#myselect").off("change").on("change", function(){
console.log("change");
});
}
Thanks in advance
Try to use namespacing:
//--- This binds the event to the element
function bindEvent(){
$("#myselect").on("change.something", function(){
console.log("change");
});
}
//--- remove any change event previously added, then rebind it
function rebindEvent(){
$("#myselect").off("change.something").on("change", function(){
console.log("change");
});
}
As #Karl said, using namespace is to:
Giving a name to your event allow you to identify that event. So when using .off, you can target a specific event to turn off.
You have to call .selectmenu("refresh") when you remove change binding. Because by default, change is attached to selectmenu as mentioned here. So if you remove it, you interrupt jQuery Mobile widget from "refreshing" to visually display the value.
See it working here.
function rebindEvent(){
$("#myselect").off("change").on("change", function(){
$(this).selectmenu("refresh");
});
}
Forgive me if I word the title wrong, I'm speculating on what my problem might be as I'm not a javascript coding expert. I have a series of divs that are generated by a php loop with unique ids created by adding the unique id contained in an auto increment column in the mysql db table that contains all the info for the row.
When the user clicks on the div this function fires off:
onclick=\"showModal('".$rowInfo['ID']."_row-id')\"
javascript code:
function showModal(ID) { /* code that shows hidden modal window */ }
This works fine, however now I need to start adding javascript buttons (in my case img tags with onclick functions) to the div with the showModal onclick function.
I added this code to the showModal(ID) function:
var downArrow = document.getElementById(ID+'_down-arrow'); // Down arrow is the button users click to show addition buttons/divs
downArrow.addEventListener('click',arrowCheck,false); // checks to see if down arrow was clicked, if so arrowCheck function runs and stops propagation.
arrowCheck function:
function arrowCheck(e) { e.stopPropagation(); }
This bit of code also works but ONLY AFTER the user has clicked the div once, the first time the div is clicked both functions fire off (ie the modal window and the extra buttons that the down arrow shows) but after the first click the addEventListener does it's job and clicking the down arrow only shows extra buttons, elsewhere brings up the modal, etc.
I'm guessing I need to create the event listener before the user clicks the div and fires off showModal(), is this correct? I'm not sure how to create a unique event listener for each down arrow image before the div is clicked, or even if I need to. Thanks for any help!
Since the event listener is being created within the modal function, you need to call the modal function before the other listener is even added.
To get your desired results, you could either directly add the onclick method to the image that the PHP code creates, or you could detect when the page is finished loading and add those listeners then. To do the latter, though, you'd need to either query for something the images all have in common, like a classname, or you'd have to keep track of the IDs used and manually add a listener for each. e.g.:
<body onload="initEventhandling()">
initEventHandling = function () {
var buttons = document.getElementsByClassName("image-button");
for (button in buttons) {
button.addEventListener('click', arrowCheck, false);
}
}
David Millar's code pointed me in the right direction. I tried to put the initEventHandling function in the onload of the body tag but I couldn't get it to work. It executed the function but I could not get the eventListeners to work. I solved it by creating an event listener using the onload for each img tag, so my code ended up like this:
<img id=\"".$appChart['ID']."_down-arrow\" onload=\"addEvent('".$appChart['ID']."')\" onclick=\"clickReviewArrow('".$appChart['ID']."')\" src='...' />
javascript:
function addEvent(ID) { var downArrow = document.getElementById(ID+'_down-arrow');
downArrow.addEventListener('click',arrowCheck,false); }
function arrowCheck(e) { e.stopPropagation(); }
David Millar may have been suggesting this, if so I'll mark his answer.
So I have a button inside a list row that is used to delete the row from the page (calls ajax stuff to delete the object represented by the row, but that's not important for my question). The whole row is bound to a click event which would redirect to another page.
In other words, the containing row is click bound and the inner button is click bound, which is causing me problems since clicking the inner button also triggers the containing row click event (as it should).
I've tried binding a hover event for all delete buttons that unbinds the row click on mouseover, and rebinds it on mouseout, like this pseudocode below:
$('.delete-button').hover(
function() {
$('.list-row').unbind();
$('.delete-button').bind('click', function() { /* delete action */ });
},
function() {
$('.delete-button').unbind();
$('.list-row').bind('click', function() { /* list row action */ });
}
);
This isn't working very well, and I'm convinced there is a better way to approach it. Should I take the button out of the containing list-row? It's way easier to have it in there since my list row contains custom attributes that have data I need for the ajax calls and I can just var rid = $('.delete-button).parent().attr('row-id'); to get the data, but I'm not opposed to change :)
Thanks!
In your click event handler for the button, you need to call e.stopPropagation(). This will prevent the event from bubbling up the DOM tree. More info here: http://api.jquery.com/event.stopPropagation/
edit: you already accepted (thanks!), but maybe this code snippet would help explain some of the concepts better:
$('.list-row').click(function() {
/* list row action */
});
$('.delete-button').click(function(e) {
// die, bubbles, die
e.stopPropagation();
// if you also need to prevent the default behavior for the button itself,
// uncomment the following line:
// e.preventDefault();
// note that if you are doing both e.stopPropagation() AND e.preventDefault()
// you should just `return false;` at the end of the handler (which is jQuery-
// sugar for doing both of these at once)
/* delete action */
})
There's a few ways of approaching this. As #jmar777 has already said you may attach an altered event to the click handler on the button, stopping propagation.
If you want to do this with the same function as you're applying to the div then you can approach it as such:
if($(event.target).is("input")) {
event.stopPropagation();
}
Another approach is to actually not bind the click event to the button, for any time the browser supports clicks on the containing element. As you will always trigger that, then you don't actually need the button to handle it too! This does require you to handle IE6 etc a little differently from everything else though...
Let your handler function return false
I have a search suggestion box that I hide when the search text box loses focus. This works great, except that when I click one of the suggestions the click event for that suggestion does not fire.
searchText.focusout(function () { $("#search-suggestions").hide(); });
I also tried:
searchText.focusout(function () { $("#search-suggestions").css("visibility", "hidden"); });
I tried commenting out the hide on unfocus code and the click events then worked fine.
(Basically, the blur event happens before the click on the suggestion can be registered, such that the element I attempted to click is not on the screen when the clicm does register)
here's the click event code:
//Called after the ajax load
$("#search-suggestions").find("a").click(function () { alert("hi"); })
I also tried rendering this on the server but it failed as well:
Search Suggestion
If any one has any suggestions I would appreciate it. Thanks!
You could try to define something like this:
//this goes where you first binding focusout handler
searchText.focusout(onFocusOut);
//this is a usual function
function onFocusOut() {
$("#search-suggestions").hide();
}
//this could be defined after you draw the search-suggestions control
$("#search-suggestions").hover(function() {
//this is hover in handler; unbind focusout from searchText
//something like that:
$("#searchText").unbind('focusout', onFocusOut)
}, function() {
//this is hover out handler; bind focusout to searchText
//something like that:
$("#searchText").bind('focusout', onFocusOut)
});
you could also use live (http://api.jquery.com/live/) to define hover handler for #search-suggestions, depending on what exactly you need.
This will make your search suggestions stay visible when clicking them. In click handler you can then hide them.
Try just making it invisible.
Change $('#my_search_box').hide(); to $('#my_search_box').css('visibility','hidden');
If you have surrounding DOM elements that need to act as if the search box is gone, you can just assign it an absolute position as well.
Try using .css('visibility', 'hidden') instead of .hide which uses display:none.