Call a function as if an event called it - javascript

I have the following function:
bringToFront : function () {
"use strict";
Desktop.appZ += 1;
this.style.zIndex = Desktop.appZ;
}
This function get's called when certain elements are clicked:
appWindow.addEventListener("mousedown", Desktop.bringToFront, false);
appWindowParent.appendChild(appWindow);
However, if I add some elements to the DOM and click them, thus increasing their z-index, and then add another element, this element will appear behind the first elements, instead of in front of them. So when I add "appWindow" to "appWindowParent", I also want to call "bringToFront" on "appWindow". I need to do this without chaining the "bringToFront" function (i.e. without adding arguments).
Thanks!
By the way, I know I could just increase the z-index manually when I create the element, but I intend to do more things in the "bringToFront" function and I don't want to duplicate that code.

You can use apply() to set the value of this inside the function
appWindowParent.appendChild(appWindow);
Desktop.bringToFront.apply(appWindow);

Detect by following code if a new element is inserted into dom
$(document).on('DOMNodeInserted', function(e) {
if (e.target.id == 'someID') {
}
});

Related

How to make an onShow onHide event for jQuery for only applied element?

I am using the following code, to enable on-show and on-hide events. So, for example when I make a DIV visible, it will automatically call my custom function and do appropriate changes on the DIV at that moment automatically.
(function ($) {
$.each(['show', 'hide'], function (i, ev) {
var el = $.fn[ev];
$.fn[ev] = function () {
this.trigger(ev);
return el.apply(this, arguments);
};
});
})(jQuery);
The problem is, it is called when any other element inside that main DIV is show()ned too.
So, when I do
$('.mother').on('show',function(){
if(...){
$(this).find('.child').show();
} else {
$(this).find('.child').hide();
}
});
for
<div class="mother">
<div class="child"></div>
</div>
It goes to infinite loop, as in jQuery on() method requires a selector for the second parameter and it is triggered for an event that happens on the children too if that second parameter is omitted or null.
I couldn't define that second parameter.
So making it
$('.mother').on('show','.mother', function(){
does not work, as it is already the mother...
I do not want to type:
if(event.target != event.delegateTarget){return;}
to every single callback function and I couldn't do it in the plugin code too as it doesn't have event object there...
These were what I have tried.
How can I solve this?
What can be the alternatives?
I want to be able to bind onShow functions to specific elements which shall never be called on events those happen on it's children.
If you want to run a custom function after show / hide on some elements and on others not you shouldn't change the $.fn.show() / .hide() itself. Instead you can pass your function as a callback parameter to .show() / .hide():
function custom() {console.log('custom: ', this);}
$(".mother").show(custom); // ---> custom: <HTMLElement>
Note that the callback is executed after show / hide have finished. So if you call them with duration:
$(".mother").show(2000, custom);
custom is executed 2 seconds later.

.class selector not working

I'm working in a card game system that the player can select the card by clicking on it and then select the place to put it on. My problem is that when the player click on the target place, nothing happens.
This is my try: http://jsfiddle.net/5qMHz/
And this is my code:
function target() {
$(".target").on("click", function() {
$("#"+x).appendTo(this);
console.log(x);
});
};
What's wrong?
Try binding with document, since you change the class during document ready and there was no element with the class target initially
$(document).on("click",".target", function() {
$("#" + x).appendTo(this);
console.log(x);
}
WORKING FIDDLE
Firstly, your practice of putting function references in to jQuery objects is rather odd. The problem however is that because the .target class is applied after DOM load you need to use a delegate selector. Try this:
var $card
$(".card").on("click", function () {
$card = $(this);
if ($(".myslot").length) {
if ($(".myslot").is(':empty')) {
$(".myslot:empty").addClass("target");
} else {
alert('No empty slots');
}
}
});
$('.field').on('click', ".target", function () {
$card.appendTo(this);
$card = $();
});
Example fiddle
At the moment you are trying to bind the event handler, the elements don't have a class target yet. From the documentation:
Event handlers are bound only to the currently selected elements; they must exist on the page at the time your code makes the call to .on().
(Technically the elements exist, but they are not (yet) addressable by the class target)
You have three options to solve this:
Add the class to your HTML markup.
Bind the handler after you added the class to the elements.
Use event delegation.
The first two don't really fit to your use case, since your are adding the class target in response to an other event and the number of elements with the class target changes over time. This is a good use case for event delegation though:
$('.field').on('click', '.target', function() {
// ...
});

Toggling click handlers in Javascript

I have an HTML button to which I attach an event, using jQuery's bind(), like so:
$('#mybutton').bind('click', myFirstHandlerFunction);
In myFirstHandlerFunction, I'd like this handler to replace itself with a new handler, mySecondHandlerFunction, like this:
function myFirstHandlerFunction(e) {
$(this).unbind('click', myFirstHandlerFunction).bind('click', mySecondHandlerFunction);
}
In the second click handler, mySecondHandlerFunction, I'd like to toggle the button back to its original state: unbind the mySecondHandlerFunction handler and reattach the original handler, myFirstHandlerFunction, like so:
function mySecondHandlerFunction(e) {
$(this).unbind('click', mySecondHandlerFunction).bind('click', myFirstHandlerFunction);
}
This works great, except for one small detail: because the click event has not yet propagated through each of the button's click handlers, the click event is passed on to the button's next click handler, which happens to be the handler that was just bound in the previous handler. The end result is mySecondHandlerFunction being executed immediately after myFirstHandlerFunction is executed.
This problem can be easily solved by calling e.stopPropagation() in each handler, but this has the negative side-effect of cancelling any other click handlers that may have been attached independently.
Is there a way to safely and and consistently toggle between two click handlers, without having to stop the propagation of the click event?
Update: Since this form of toggle() was removed in jQuery 1.9, the solution below does not work anymore. See this question for
alternatives.
It looks like toggle() would solve your problem:
$("#mybutton").toggle(myFirstHandlerFunction, mySecondHandlerFunction);
The code above will register myFirstHandlerFunction and mySecondHandlerFunction to be called on alternate clicks.
Just use a boolean to toggle the functionality of the handler, there's no need to juggle which handler is listening:
$('#mybutton').bind('click', myHandlerFunction);
var first = true;
function myHandlerFunction(e) {
if(first){
// Code from the first handler here;
}else{
// Code from the second handler here;
}
first = !first; // Invert `first`
}
This solution is a hack, but it is short and sweet for your rough work:
$('#myButton').click(function() {
(this.firstClk = !this.firstClk) ? firstHandler(): secondHandler();
});
It's a hack because it's putting a new property directly onto this which is the click-target HTML DOM element, and that's maybe not best practice. However, it thus avoids creates any new globals, and it can be used unchanged on different buttons simultaneously.
Note that the first part of the ternary operation uses = and not == or ===, i.e. it's an assignment, not a comparison. Note also that the first time the button is clicked, this.firstClk is undefined but is immediately negated, making the first part of the ternary operation evaluate to true the first time.
Here's a working version:
$('#a > button').click(function() {(this.firstClk = !this.firstClk) ? a1(): a2();});
$('#b > button').click(function() {(this.firstClk = !this.firstClk) ? b1(): b2();});
function a1() {$('#a > p').text('one');}
function a2() {$('#a > p').text('two');}
function b1() {$('#b > p').text('ONE');}
function b2() {$('#b > p').text('TWO');}
div {display: inline-block;width: 10em;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="a"><p>not yet clicked</p><button>click</button></div>
<div id="b"><p>NOT YET CLICKED</p><button>CLICK</button></div>
I was looking at this today, and realized there was still not a way to do this without a global variable listed. I came up with the following to add locations to an ESRI basemap, but it would work generally too:
function addLocationClickHandler() {
var addLocationClick = overviewMap.on('click', function (event) {
addLocationClick.remove();
})
$('#locationAddButton').one('click', function (cancelEvent) {
addLocationClick.remove();
$('#locationAddButton').on('click', addLocationClickHandler)
});
}
$('#locationAddButton').on('click', addLocationClickHandler)
This should allow you to put something else in the section where you overwrite the click handler and not necessitate a global variable.
This would help add data-click-state attribute on your button
$(document).ready(function() {
$('#mybutton').on('click', function() {
if ($(this).attr('data-click-state') == 1) {
$(this).attr('data-click-state', 0)
myFirstHandlerFunction();
} else {
$(this).attr('data-click-state', 1)
mySecondHandlerFunction();
}
});
});
Like this:
$(this).bind('click', myMasterHandler);
handler = 0;
function myMasterHandler(e) {
if(handler == 0) {
myFirstHandler(e);
handler = 1;
} else {
mySecondHandler(e);
handler = 0;
}
}

jQuery: Get reference to click event and trigger it later?

I want to wrap an existing click event in some extra code.
Basically I have a multi part form in an accordion and I want to trigger validation on the accordion header click. The accordion code is used elsewhere and I don't want to change it.
Here's what I've tried:
//Take the click events off the accordion elements and wrap them to trigger validation
$('.accordion h1').each(function (index, value) {
var currentAccordion = $(value);
//Get reference to original click
var originalClick = currentAccordion.click;
//unbind original click
currentAccordion.unbind('click');
//bind new event
currentAccordion.click(function () {
//Trigger validation
if ($('#aspnetForm').valid()) {
current = parseInt($(this).next().find('.calculate-step').attr('data-step'));
//Call original click.
originalClick();
}
});
});
jQuery throws an error because it's trying to do this.trigger inside the originalClick function and I don't think this is what jQuery expects it to be.
EDIT: Updated code. This works but it is a bit ugly!
//Take the click events off the accordion elements and wrap them to trigger validation
$('.accordion h1').each(function (index, value) {
var currentAccordion = $(value);
var originalClick = currentAccordion.data("events")['click'][0].handler;
currentAccordion.unbind('click');
currentAccordion.click(function (e) {
if ($('#aspnetForm').valid()) {
current = parseInt($(this).next().find('.calculate-step').attr('data-step'));
$.proxy(originalClick, currentAccordion)(e);
}
});
});
I think this:
var originalClick = currentAccordion.click;
Isn't actually doing what you think it is - you're capturing a reference to the jQuery click function, rather than event handler you added, so when you call originalClick() it's equivalent to: $(value).click()
I finally came up with something reliable:
$(".remove").each(function(){
// get all our click events and store them
var x = $._data($(this)[0], "events");
var y = {}
for(i in x.click)
{
if(x.click[i].handler)
{
y[i] = x.click[i].handler;
}
}
// stop our click event from running
$(this).off("click")
// re-add our click event with a confirmation
$(this).click(function(){
if(confirm("Are you sure?"))
{
// if they click yes, run click events!
for(i in y)
{
y[i]()
}
return true;
}
// if they click cancel, return false
return false;
})
})
This may seem a bit weird (why do we store the click events in the variable "y"?)
Originally I tried to run the handlers in x.click, but they seem to be destroyed when we call .off("click"). Creating a copy of the handlers in a separate variable "y" worked. Sorry I don't have an in depth explanation, but I believe the .off("click") method removes the click event from our document, along with the handlers.
http://www.frankforte.ca/blog/32/unbind-a-click-event-store-it-and-re-add-the-event-later-with-jquery/
I'm not a jQuery user, but in Javascript, you can set the context of the this keyword.
In jQuery, you use the $.proxy() method to do this.
$.proxy(originalClick, value);
originalClick();
Personally, I'd look at creating callback hooks in your Accordion, or making use of existing callbacks (if they exist) that trigger when opening or closing an accordion pane.
Hope that helps :)
currentAccordion.click is a jQuery function, not the actual event.
Starting with a brute-force approach, what you'd need to do is:
Save references to all the currently bound handlers
Unbind them
Add your own handler, and fire the saved ones when needed
Make sure new handlers bound to click are catched too
This looks like a job for an event filter plugin, but I couldn't find one. If the last point is not required in your application, then it's a bit simpler.
Edit: After some research, the bindIf function shown here looks to be what you'd need (or at least give a general direction)

Passing argument to JS function from link onclick

I have a link that looks like this:
<a id="mylink" onclick="deleteHike( 3 );" href="javascript:void(0);">Yes</a>
It is able to call this JavaScript:
window.onload = function()
{
//Get a reference to the link on the page
// with an id of "mylink"
var a = document.getElementById("mylink");
//Set code to run when the link is clicked
// by assigning a function to "onclick"
a.onclick = function( hike_id )
{
// Somecode her
// But when I try to use the hike_id it displays as [object MouseEvent]
}
}
But the value that comes in is [object MouseEvent], not the number that I was expecting. Any idea why this happens and how to fix this? :)
Thanks!
You are trying to assign the function to your link in two different and conflicting ways.
Using the eval-ed function string, onclick = "function(value)", works but is deprecated.
The other way of binding the click handler in the onload event works too, but if you want a particular value to be passed, you'll have to change your script a bit because the value as given in the initial onclick is completely lost when you set the onclick to a new function.
To make your current method work, you don't need an onload handler at all. You just need this:
function deleteHike(hike_id) {
// Some code here
}
To do it the second way, which I recommend, it would look like this:
<a id="mylink" href="javascript:void(0);">Yes</a>
with this script:
function deleteHike(e, hike_id) {
// Some code here
// e refers to the event object which you can do nifty things with like
// - learn the actual clicked element if it was a parent or child of the `this` element
// - stop the event from bubbling up to parent items
// - stop the event from being captured by child items
// (I may have these last two switched)
}
function getCall(fn, param) {
return function(e) {
e = e || window.event;
e.preventDefault(); // this might let you use real URLs instead of void(0)
fn(e, param);
};
}
window.onload = function() {
var a = document.getElementById("mylink");
a.onclick = getCall(deleteHike, 3);
};
The parameter of a DOM event function is the event object (in Firefox and other standards-compliant browsers). It is nothing in IE (thus the need to also grab window.event). I added a little helper function for you that creates a closure around your parameter value. You could do that each time yourself but it would be a pain. The important part is that getCall is a function that returns a function, and it is this returned function that gets called when you click on the element.
Finally, I recommend strongly that instead of all this, you use a library such as jQuery because it solves all sorts of problems for you and you don't have to know crazy JavaScript that takes much expertise to get just right, problems such as:
Having multiple handlers for a single event
Running JavaScript as soon as possible before the onload event fires with the simulated event ready. For example, maybe an image is still downloading but you want to put the focus on a control before the user tries to use the page, you can't do that with onload and it is a really hard problem to solve cross-browser.
Dealing with how the event object is being passed
Figuring out all the different ways that browsers handle things like event propagation and getting the clicked item and so on.
Note: in your click handler you can just use the this event which will have the clicked element in it. This could be really powerful for you, because instead of having to encode which item it was in the JavaScript for each element's onclick event, you can simply bind the same handler to all your items and get its value from the element. This is better because it lets you encode the information about the element only in the element, rather than in the element and the JavaScript.
You should just be able to declare the function like this (no need to assign on window.onload):
function deleteHike(hike_id)
{
// Somecode her
// But when I try to use the hike_id it displays as [object MouseEvent]
}
The first parameter in javascript event is the event itself. If you need a reference back to the "a" tag you could use the this variable because the scope is now the "a" tag.
Here's my new favorite way to solve this problem. I like this approach for its clarity and brevity.
Use this HTML:
<a onclick="deleteHike(event);" hike_id=1>Yes 1</a><br/>
<a onclick="deleteHike(event);" hike_id=2>Yes 2</a><br/>
<a onclick="deleteHike(event);" hike_id=3>Yes 3</a><br/>
With this JavaScript:
function deleteHike(event) {
var element = event.target;
var hike_id = element.getAttribute("hike_id");
// do what you will with hike_id
if (confirm("Delete hike " + hike_id + "?")) {
// do the delete
console.log("item " + hike_id + " deleted");
} else {
// don't do the delete
console.log("user canceled");
}
return;
}
This code works because event is defined in the JavaScript environment when the onclick handler is called.
For a more complete discussion (including why you might want to use "data-hike_id" instead of "hike_id" as the element attribute), see: How to store arbitrary data for some HTML tags.
These are alternate forms of the HTML which have the same effect:
<a onclick="deleteHike(event);" hike_id=4 href="javascript:void(0);">Yes 4</a><br/>
<button onclick="deleteHike(event);" hike_id=5>Yes 5</button><br/>
<span onclick="deleteHike(event);" hike_id=6>Yes 6</span><br/>
When you assign a function to an event on a DOM element like this, the browser will automatically pass the event object (in this case MouseEvent as it's an onclick event) as the first argument.
Try it like this,
a.onclick = function(e, hike_id) { }

Categories

Resources