I have browsed the internet extensively on this issue, including stackoverflow.
My problem is that I have a set of 'li', and I want multiple 'li' added to an array when I use ctrl+click gesture. I keep on getting (e) is not defined. I have found this: Detect CTRL and SHIFT key without keydown event?
But the answer provided, which seems to have worked for many, doesn't for me. Whenever I use that, even as the sole item in my script, firebug doesn't respond in the console, but I get: " ReferenceError: e is not defined." I'm using Firefox.
My biggest problem is getting this to add this to a function, and the function, which fires as an event, can distinguish between the a ctrl+click and normal click.
Any expertise to help me out? Vanilla Javascript preferred.
The point of this exercise is to remove the LI when clicked, but I want to delete multiple at once if I hold down ctrl. Perhaps by storing them in an array.
EDIT: Some Code
<ul id = "ulItem">
<li>item1</li>
<li>item2</li>
<li>item3</li>
<li>item4</li>
</ul>
<script>
window.onload = function(){
var ulItem = document.getElementById("ulItem"); //gets UL with the "ulItem" ID.
var ulList = ulItem.getElementsByTagName("li"); //gets ulItem's "li" in an array.
///prepareLI function.///
var prepareLi = function(){
for(i = 0; i < ulList.length; i++){
ulList[i].addEventListener('click', elementClick);
}
}
//adds the same event listener to each of the "li" inside "UlList" array. Each activated by a click.
///elementClick function.///
var elementClick = function(){
ulItem.removeChild(this);
} //if this is a child of parent, UlList, remove it.
prepareLi();
}
The browser is correctly telling you that you never declared e, while the example in Detect CTRL and SHIFT key without keydown event? has defined e, by declaring it as a formal argument to the listener function.
var elementClick = function(e){
if(e.ctrlKey) {
ulItem.removeChild(this);
}
}
Note the use of function(e){ rather than function(){
I think I found an alternative way to do this.
If you want to emulate a Ctrl+click in order to select multiple objects, this worked for me as a workaround:
make two variables, loader and loaderArray:
var loader = 1;
var loaderArray = [];
set a document event:
document.addEventListener('mousedown', MultiSelect);
and a click event on the item:
document.addEventListener('click', singleSelect);
Make sure the window event is is a mousedown item, also, that Ctrl is defined as keyCode == 17. The number part is VERY important. Here is code I used:
function loadArray(e) {
if (e.keyCode === 17) {
//console.log("key down");
loader = 2;
};
}
Then I set a separate function on the singleSelect event stating that if loader ==2, use loaderArray.push(this) rather than the normal way of doing things when loader == 1.
The loader Array then collects the variables, and you can do whatever you want with the array using for() or some other loop.
Now, whenever I click the Ctrl key, the mousevent on the document turns the loader variable to 2, and it can distinguish between Ctrl+click and normal click this way.
Thanks for all those that helped, and I hope this will help others! :)
Related
I am adding some elements dynamically to a page when making this chrome extension. It is a list added like this
var result = document.createElement("UL");
var checkoutArea = document.getElementById("shipping");
result.setAttribute("id", "resList");
if (document.getElementById("resList") === null) {
checkoutArea.appendChild(result);
}
I am also adding <'li'> child to it later. But when I am trying to add onclick function to each item, it does not work. I checked when I click on the item dynamically generated, and it seems that those items are not clicked, as this gives wrong results:
document.addEventListener('click', function(e) {
e = e || window.event;
var target = e.target || e.srcElement,
text = target.textContent || text.innerText;
console.log(text);
}, false);
a screenshot of the DOM of dynamically generated list
Also when I 'inspect' on that 'home' item, chrome is giving me that highlighted element, which is the 'save & continue' button besides. I am thinking there's an overlap, so how should add any 'onclick' function to those items?? Great thanks in advance.
You probably need to try the Event delegation pattern. The idea is that if we have a lot of elements handled in a similar way, then instead of assigning a handler to each of them – we put a single handler on their common ancestor.
Read about it on this link: http://javascript.info/event-delegation
I managed to understand Event delegation thanks to video 15 that has the name LocalStorage and Event Delegation in the course javascript30 of Wes Bos. The course is free and worthwhile. I hope this information serves you
So i'm trying to create a js/css "wave game" like tower defense ones.
When all the pre-generated enemys from first wave are dead, it spawns the second wave and so on.
So far so good.
The problem is that i just can't attack mobs dynamically spawned within second wave.
I used to try .live() in similar cases, but its deprecated, so i'm trying .on(), as instructed
$('.enemy').on('mousedown' , function(event) {
//attack code
}
Its working fine for initial mobs (1st wave) but it still just not working on dynamic mobs (>= 2nd wave)
Help, guys, please?
You need to specify an element that is already there when the DOM is created. In the parameters, you specify the elements you want to add the mousedown method. By simply assigning $('.enemy'), it will attach the method to those that are already present in the DOM.
$('body').on('mousedown', '.enemy', function(event) {
//attack code
}
As Wex mentioned in the comments, instead of writting $('body') you should use the container's name (the container which wraps the .enemy elements. This way, when a .enemy element is added, the event doesn't need to bubble all the way up to the body tag.
The binding '.on()' works only with the content that created earlier then the script ran.
So one solution could be you bind the event to the parent element.
$('.PARENT_ELEMENT').on('mousedown', '.enemy', function(event){
// your code here
}
That should do it.
I made this google like drop down suggestions search box and I faced a problem similar to yours where there was suggestions disappearing before the re-direct happened. I overcame it by using and modifing vyx.ca answer:
var mousedownHappened = false;
var clicked_link;
$("#search-box").blur(function(e) {
if (mousedownHappened)// cancel the blur event
{
mousedownHappened = false;
window.location.href = clicked_link;
} else {
// no link was clicked just remove the suggestions box if exists
if ($('#search-btn').next().hasClass('suggestions')) {
$(".suggestions").remove();
}
}
});
//attaching the event to the document is better
$(document).on('mousedown', '.suggestions a', function() {
clicked_link= $(this).attr('href');
mousedownHappened = true;
});
I have looked high and low and can't seem to find this anywhere. Does anyone know how to get the value of a row tapped in a listview? This can be anything from the name to the index in the object. Right now I have a function that handles the tap. I need to be able to pass a value to the new page I am loading when it transitions. I thought I could do it here:
$('#taskListTable').delegate('li', 'tap', function () {
console.log('clicked');
//Insert code here to pull a value from either an index or a name and save it
});
I thought maybe it would be good to do it in the hash? I am not sure what the standard practice is here on the web coming from native iOS dev though. Anyone have any pointers? Thanks.
This is how I am populating my listview:
$.each(tasks, function(index, task) {
$taskList.append("<li><a href='taskDetails.html'> <h3>"+task.name+"</h3><p>"+task.description+"</p></a></li>");
});
taskDetails.html needs the index of the task so I can pull the details down from the server. What is the standard practice for doing that?
To get the index of the taped list-item you can do this:
$('#taskListTable').delegate('li', 'tap', function () {
console.log('clicked');
var index = $(this).index();
});
Yup, that's it. Although this assumes that the <li> element are all siblings.
Docs for .index(): http://api.jquery.com/index
If you want to then transition to the new page:
$('#taskListTable').delegate('li', 'tap', function () {
console.log('clicked');
$.mobile.changePage($(this).find('a').attr('href'), {
data : { selectedIndex : $(this).index() }
});
});
This will get the new page and attach the selectedIndex variable as a query string parameter that is set to the index of the tapped list-item.
Also, to be able to prevent the default behavior of clicking on the link in the list-item, I would attach this event handler to the link elements:
$('#taskListTable').delegate('a', 'tap', function (event) {
event.preventDefault();
console.log('link clicked');
$.mobile.changePage($(this).attr('href'), {
data : { selectedIndex : $(this).closest('li').index() }
});
});
The "delegate" method is deprecated on new versions of jQuery.
Consider using this instead:
$('#taskListTable').on('tap', 'a', function (event) {
}
Here is another method of getting an id, especially useful if you have a database enabled app and the id you are searching for is the database's id, not the row index:
When filling the table consider this:
<ul id="taskListTable">
<li id="task400" class="tasks">Task with db id 400</li>
<li id="task295" class="tasks">Task with db id 295</li>
</ul>
$('#taskListTable').on('tap', 'li', function (event) {
variable = $(this).attr('id').substr(4);
}
That will get the database id which you can pass on to your other pages.
To get the name out, you could do:
$(this).find('h3').html()
Alternatively, you can use something in the markup like the id or a data attribute to provide a better handle than the name.
What about this approach?
$taskList.append("<li><a href='taskDetails.html?id=" + task.id + "'></a><h3>" + task.name + "</h3>...</li>");
And then retreive id parameter.
Jasper's solution is good, but if you have any other code in your event handler that edits the DOM, the "this" keyword could be pointed somewhere entirely different by the time it gets used in your code.
I've cracked my head on my keyboard a number of times because my users said "I click on item one, but that always opens the edit screen for the last item in the list." So somehow using 'this' was not the right way to go.
Also, when I tried event.target, event.currentTarget or event.originalTarget, those didn't work either. They all pointed to the jQueryMobile "page" that was visible by the time the code got to run (which wasn't even the same page where the table was located).
The safe and/or intended approach is to:
use event.originalEvent.srcElement instead of 'this'
not use .delegate(), but use .on()
bind the event using $(document).on(), not $('#table').on()
So that would result in:
$(document).on('tap','#taskListTable li',function(event){
//The properties of 'event' relate to $(document), not to '#taskListTable li',
//so avoid .currentTarget, .target etc.
//But fortunately 'event' does have one property that refers to the original
//user event.
var theOriginalTapEvent = event.originalEvent;
var theRealClickedElement = theOriginalTapEvent.srcElement;
//Note that the clicked element could be a sub-element of the li
//whose index you want. So just referencing theRealClickedItem could
//could still be getting you bad results.
var theRelevantListItem = $(theRealClickedElement).parents('li');
//Now you're ready to get the item's index or whatever.
})
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)
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) { }