Some Quick Background
I'm building an iOS app that uses html web views in some areas. I wanted to make sure the html buttons on those pages looked and felt as much like iOS buttons as possible. To accomplish this, I wanted a tap state so the html button depressed when tapped. Now in HTML this is easy. You just set a style for :active or :hover or whatever. I actually had this defined already. In iOS, however, those states don't engage on tap – at least normally. So my goal was to write a little script that added a class to the button to change its appearance ontouchstart.
The Issue
However, it turns out I didn't need to get that complicated...by pure accident I ran a test with the following code:
document.addEventListener('touchstart', function(event) {
console.log("test");
}, true);
I'm pretty green with javascript & jQuery, so all I intended on doing was checking my syntax and making sure the eventListener fired when I tapped the button. To my surprise, the button's :active states in the css fired (as well as the :hover states). That code...solved my problem!
My Question
So here's my question: Is the above code valid? I mean, is it bad to do this? It's as if the empty eventListener just triggered behavior that desktop browsers already offer. Is there anything wrong with using this method? I'm green, but I don't want to pick up bad habits. If this is a bad way to code I don't want to use it.
Thanks for any insight you guys can give me into this!
I personally don't think anything's wrong with empty event listeners (save for the overhead of a function call --- which sounds negligible here anyway). I'd suggest you leverage jQuery's noop function though if you must add an event listener but have it do nothing:
$(document).on('touchstart', $.noop);
// or
document.addEventListener('touchstart', $.noop);
Related
I'm working on a software that got quite huge over many years and we've noticed there are many buttons (or clickable elements like <a> and <img>) that aren't safe from double clicks. Since the software is running on sometimes quite laggy hardware (with touch screens that might bug out and register double clicks for no apparent reason) I'd like to implement some kind of global solution for it.
My first thoughts were:
Global click listener that gets the click event, processes it and starts a cooldown on that specific element. If another click event is registered before the cooldown is over, it'll just block the event.
Changing the click() prototype method of a button or something. I'm not that good with plain JS but I've done something like that for plugins before so I know at least conceptually how that works.
Adding a directive that can be inserted into existing elements which need double click protection. This would probably be the "scalpel" method, even though people might just forget to add it. Sadly I have no idea whether my idea is actually possible with directives as I've never implemented one before.
Something like a class that can be inherited which handles all clicks. Might be possible to implement together with solutions (1) and/or (2).
Do you have a direction you can point me to to investigate further? Is some kind of global handling for this a good idea at all? Are any/all of the solutions possible at all?
I want to create a "confirm plugin" that will fire first and ask the user if "they are sure". Just to be clear, I will be using a custom made confirm box, not a the default Window confirm() Method.
If yes then it will fire all the other events that have been bound to it. If no then it will do nothing.
A use case would be a delete button that has a separate click event bound to it, which when pressed will delete an element.
If I attach my plugin to the button then it will bind another click event and by using the events info inside $._data I can send my even to the top of the list (making it fire first), I then stop propagation (this stops the other binding firing which deletes the element). If the user clicks ok on my confirm box, I trigger a click again this time just bypass the stop stuff and it will then fire the original events
I am using a slightly modified version of https://github.com/private-face/jquery.bind-first
The only way it can access this info on an element is by using:
$._data($(this)[0]).events
I want to know how "future proof" this is as I know this changed already since 1.7. Are there any plans to officially support a similar thing.
If all else fails, I know I can just make sure that the plugin and the bindings happen first in the code, but this is not really the most flexible solution.
Using $._data is a smelly solution, hence this post. Maybe there are some fancy custom event things I can do?
The short and simple answer is not at all. Using, or more importantly relying on undocumented features is never a good idea.
It sounds like you have an XY Problem here. There are likely many other ways to achieve what you're trying to do here, and using $._data is almost certainly not the best solution.
For a few years now I use user-JavaScript to put additional input buttons and clickable span-elements on pages. Usually I manage to make this work, e.g.
span = document.createElement("span");
span.onclick = __oujs.onClickAddPage;
span.appendChild(document.createTextNode("Add page"));
containingDiv.appendChild(span);
Usually __oujs.onClickAddPage() is called when I click on that span-element.
However, yesterday a site made some changes (apparently I have no clue what they were) that causes clicking on my elements to not cause any events. In the example above __oujs.onClickAddPage() is not called any more. The same is true for input-elements of type "button".
As I'm using Opera, DragonFly shows that my span still is the top-most element in that particular area and, therefore, it should handle the click-event. However, I understand that they include jQuery, which might be part of the misery.
Is there a special technique (maybe with a name that Google knows of) they use to able to do such thing? How do I get the control back and have my code called again? Can I remove some object?
I'm sorry for asking in a rather broad style, but I have no clue what I can look for to fix this myself. Please ask if you need to know something.
I would suggest you this steps:
Create the next function:
function stubFn(event){
console.log('event caught', event); // this will log the click event
__oujs.onClickAddPage.call(event.currentTarget, event); // emulate the onclick behavior
}
Use span.addEventListener('click', stubFn) to add the listener to the element in your code.
If it does not work, then you have to reverse-engineer the script and markup.
I'd suggest to check if there is any element with absolute or fixed position overlapping your span. It can prevent the event propagation.
In general, there are no ways to forbid the elements from userscripts to handle events using inlined handlers.
To get this off my open questions I answer this myself rather than waiting for it to be closed:
I'm sorry, it was my fault. I had a stupid mistake in another user-JavaScript file that affected all sites...
I reinstalled my browser and was thinking about reinstalling my OS, but luckily this isn't necessary.
I am trying to do all dom manipulations off screen and then make it visible. Which works, except now I have the situation where I am trying to do it with a form which I want to focus on the first input text upon rendering it on the browser.
Something like: myForm.prependTo(myDiv).show().find('input:first').focus();
Problem is that the focus is being called before the form has finished rendering which is causing the lovely error 'Can't move focus to the control because it is invisible, not enabled, or of a type that does not accept the focus'
How do other web developers handle the similiar situation of manipulating elements off screen and then making it visible? I wish jQuery had something like myForm.prependTo(myDiv, function() { /* on render code here */ })
I know one way of doing it is setting a timeout and when it fires I put focus on the input, but I feel like that's not really the cleanest way to do things. I know the iframe has an onload event, so I'm curious if people usually draw their elements in some hidden iframe and listen for its load event to know when the element has finished rendering? If so could you point me to an example of doing this?
myForm.prependTo(myDiv).show(function(e){
$(this).find('input:first').focus();
});
I know I'm 7 years late, but I had a similar problem, which I solved by putting the stuff I needed to happen after the render in a ready handler.
I had a restore function that worked, but there was zero or near zero visual feedback that the element had been restored.
I tried emptying the element first. It still worked, but still had zero visual feedback.
$("#someSelector").empty();
restore();
Then I discovered ready() happens after the rendering; so I changed it to something like....
$("#someSelector").empty().ready(function() {
restore();
});
Now the restore() doesn't happen until after the empty() action RENDERS. This means my element APPEARS to empty out and then refill (it always did, but now the user can see it happen).
I found this solution somehow a few days ago for a different problem with some vague search that I can't remember. Then I needed it again but couldn't exactly remember what I did. Now my searches included the word "jquery" and "render" and lead me here.
I ended up going thru my code to find out what I did, and I thought it might be a good idea to post it here in case other people stumble on this post and actually need to execute something AFTER rendering happens.
Cheers.
So I have looked through most of the facebook questions here and it has absolutely confirmed my thoughts. Facebook development may be among some of the worst I've ever used. I'll avoid my rant for now, but as a note to where I'm coming from: tried php sdk, worked decently out of the box, found out I need to put the functionality on a pages tab, can't iframe on pages tab, have to use FBML (which they are retiring in two months, yet I can't start testing the iframe approach yet)
Anyway, I run into FBJS at this point. It works "good enough" for most things, but for some reason I can't get an event registered to an object (a select box in particular interest) by adding a listener (as per FBJS documentation). I am able to add it directly to the html object and have it work, but this is not desirable and I would really like to know what I consider the proper way to do it, which involves seperation of logic and display.
What I want to happen: click on the select box, make a selection, display the text of the selection in an empty div (later on adding Ajax but one step at a time here)
Code:
<script>
var obj = document.getElementById('select-id');
obj.addEventListener('onchange',my_func);
function my_func(evt){
var inner = document.getElementById('div-id');
inner.setTextValue('hey'); // for testing purposes
}
</script>
The above code doesn't do anything when I make a change to the select box. However, this behaves as planned:
<select name="find_state" id="find_state" onchange="my_func();">
I will be grudgingly using this method as I develop, but would really love to know what I might be doing wrong or if anyone else has this issue? And if anyone has any opinions on the matter I would love to know of some form of facebook development recommendations as applications, pages, and tabs all appear to behave totally different from eachother, yet it seems that they all should be doing the same thing to me? Is there any unified way to develop across all three of these things, or am I missing something?
Thanks in advance, as well as for the past help!
I think it should be:
obj.addEventListener('change',my_func);
(instead of onchange)
Straight from Facebook documentation:
The third parameter [to addEventListener], boolean useCapture is required (it does not have a default value)
That means that you should have:
obj.addEventListener('change', my_func, false);
Use the following html and your events attached with .addEventListener() start to work. This seems to be undocumented "feature".
<select name="find_state" id="find_state" onmousedown="return true;">
This also enables the event to fire first time the user changes the value of select. Otherwise it would fire only on second onchange event.