clearing :active pseudo-class on current element with JavaScript - javascript

I've implemented my own kinetic scrolling component that generally works very well. My problem is that link elements in the page that use the :active pseudo-class maintain their :active state even when the user swipes and thus scrolls the screen (which means that the mouseup won't generate a "click").
Currently I already can avoid the "click" event but the visual feedback (:active) does not match the behavior.
So I need to directly or indirectly clear the ":active" via JavaScript. Perhaps creating a dummy link and "activate" it via JavaScript would solve the problem, but I had no luck with that.
To find a solution a made a simple testcase that demonstrates this: http://jsfiddle.net/LkAXd/2/
Any ideas?
Note I just need a solution that works with Webkit.
Update
This dirty hack clears the :active pseudoclass from the element l1 (basically by briefly removing it from the document):
var next = l1.nextSibling;
document.body.removeChild(l1);
document.body.insertBefore(l1, next);
The problem is that document.activeElement apparently does not reference links that just got a mousedown (they don't get focus that way), so I have no way to know which element currently is :active.

I'm not sure if I fully understood what you're trying to do, but you can get the active element using document.querySelector.
Adding the following code to your test case worked for me:
var aEl = document.querySelector("a:active"), // Active Element
nEl = aEl && aEl.nextSibling, // The node following it
pEl = aEl && aEl.parentNode; // The parent node
if (aEl && pEl) {
pEl.removeChild(aEl);
pEl.insertBefore(aEl, nEl);
}
Working demo: http://jsfiddle.net/AndyE/LkAXd/3/
Since you tagged webkit, I'm assuming you're in control of the environment and don't need to worry about legacy browsers.

Related

How to find out and control which elements gets scrolling keyboard events?

HTML has scrollable elements. At any given time one of them is going to receive keyboard scrolling controls like up/down arrow, pageup/pagedown etc.
How can I find out which element is currently active in this way, and how can I make an element active in this way?
Here's jsfiddle to demonstrate the issue.
I can click on e1, e2, e3, or e4. If I do, then arrow keys will scroll that element.
The obvious way to do this would be to call element.focus(). But that does nothing when called on a scrollable div, and document.activeElement keeps pointing to body not to any of them (the one that's actually active, or the one I tried to make active).
So:
is there some other DOM thing which controls it?
or is this functionality not exposed by browsers in any way?
or do I need to set a bunch of tabindex settings to access this state through JS, even though browsers can handle active scrolling status without any tabindex by theselves?
Use Event Listeners To Do So
function gg1(){
document.getElementById("focused").innerText = "e1"
}
function gg2(){
document.getElementById("focused").innerText = "e2"
}
document.getElementById("e1").addEventListener("scroll", gg1);
document.getElementById("e2").addEventListener("scroll",gg2);
This Code Is Just The Basic Version Of How You could achieve it. You can always minify it.

Optimising Jquery code - Adding and removing classes on click

I am currently learning js and jquery to assist me with my designs, a common problem that I am having is that I can get it to do what I want it to but I have no idea if the way in which it has been coded is efficient. Could anyone see a better way to code this:
$(".cal-check a").click(function(e) {
e.preventDefault();
$(".agenda").addClass("active");
});
$(".agenda .close-panel").click(function(e) {
e.preventDefault();
$(".agenda").removeClass("active");
});
I want to click on a calendar event then it adds the class active to another class within the calendar called agenda which then brings up the agenda. I then remove it by clicking on a close panel element. Many thanks
You could cache the .agenda selector like so:
var $agenda = $(".agenda");
$(".cal-check a").click(function(e) {
e.preventDefault();
$agenda.addClass("active");
});
$agenda.find(".close-panel").click(function(e) {
e.preventDefault();
$agenda.removeClass("active");
});​
I recommend not changing classes, that will usually be rather intensive on the browser, because one class change will mean that all the classes have to be reparsed. This is usually very bad for more aggressive stuff, like animation, but if you have performance considerations, you should take that into advisement.
I think this is already efficient.
Just something that might help in the future is to try and dive into the DOM as little as possible. In this small example it wont make a difference but for example create a variable for agendaClass instead of using jquery every time to fetch it.
var agendaClass = $(".agenda");
$(".cal-check a").click(function(e) {
e.preventDefault();
agendaClass .addClass("active");
});
$(".agenda .close-panel").click(function(e) {
e.preventDefault();
agendaClass .removeClass("active");
});
That should be efficient enough. How you can optimize your code strongly depends on your DOM structure.
Behind the scene, jQuery with its Sizzle search engine will use built in methods for DOM search, if those are available (native search will be always faster than search done with JS). In your case everything should be Ok, especially in modern browsers, as they have querySelectorAll and .cal-check a and .agenda selectors will be executed with that built in method. Also, there is getElementByClassName which could be used to find .agenda.
Both of those methods are supported by most of modern browsers (provided links have a list of supported browser), so talking about browsers like IE8+, Firefox and Chrome will be fast enough with your selectors. At the same time IE7 has no functions like above and Sizzle will be forced to go through numerouse elements to find elements you are looking for. Maybe you can limit that amount specifying some container with id, in that case it will look inside that elements only:
$("#someId .agenda"), for instance. You may want additionally add some tag: $("#someId div.agenda"). This way you will limit amount of elements to search with divs (getElementsByTagName could be used) inside #someId (getElementById). That way you may increase speed in IE7 and other old browsers with no support of getElementByClassName and querySelector
Plus, you may cache search results as it was already mentioned here.

onclick event not firing javascript

EDIT #2:
Made a JS Fiddle... http://jsfiddle.net/N2p6G/ (I hardcoded some stuff that I'm certain works correctly, but the problem is still there)
Original:
So, I have written tens of thousands of lines of javascript, and used code that look like this a hundreds of times and I don't understand what's going on.
blacklistitembutton.onclick = function() {
console.log("clicked.");
}
The above code does not seem to be working... and I can't figure out why
In fact, I use the same method earlier in the same file... and it works fine!
settings.onclick = function() {
settings_popup.toggle();
}
EDIT:
Might it have something to do with the fact that it's being executed in a for loop?
Here is the code...
var blacklistButton = document.createElement('input');
blacklistButton.type = 'button';
blacklistButton.value = "Add Current Site to Blacklist";
blacklistButton.onclick = function() {
console.log('blacklistButton clicked');
}
for (var i=0;i<blacklist.length;i++) {
var blacklistitembutton = document.createElement('div');
blacklistitembutton.type = 'button';
blacklistitembutton.blacklistValue = blacklist[i];
blacklistitembutton.value = "X";
blacklistitembutton.onclick = function() {
console.log("clicked.");
}
}
Then both blacklistButton and all of the blacklistitembuttons are put into the document through element.appendChild (and they all show up successfully!)
The blacklistButton onclick fires just fine, and the blacklistitembutton onclick does not.
document.addEventListener('click', function(){
console.log('clicked');
}, false);
Edit:
Here is a re-write of your code in a fiddle: http://jsfiddle.net/N2p6G/1/
There are a lot of things in your code that worry me. Hopefully from my re-write you can see there are better ways to handle some things.
1) I'm not sure why you are using document.write() at the beginning. That has very little purpose.
2) You are modifying the DOM way too much. Some of the DOM elements you are creating in code are better-served as just being target locations in html. Only the dynamically-created input button elements need to be done in javascript. Remember, modifying the DOM should be done as little as possible.
3) Don't assign events using the onclick, onsubmit, onhover, etc syntax. Events should only be bound to DOM elements using addEventListener. The other benefit of doing it the proper way is that you can assign multiple events of the same type, if need be, to the same element. Also, with some extra state code that I haven't included, you can selectively remove particular events later if you need to.
4) There was a debate several years ago about whether using innerHTML and string templates was faster/better than using DOM creation methods. For a while, the best solution was to use documentFragments and a combination of the two methods. These days, it doesn't really matter anymore since all browsers are pretty damn fast, so for simplicity's sake is good to just go with innerHTML.
This also goes back to the rule of "don't touch the DOM too much". If you look at my code, you can see that I'm assembling the final html simply as an array of elements that gets joined as a single string at the end. Its then rendered to the DOM with a single innerHTML statement. I'm only touching the DOM one time, instead of multiple times.
5) The last bit goes into events again. At the beginning and end of the code you can see where and how I've added the events for the DOM elements. Indeed, the addEventListener at the beginning could be moved to the end to group all the event declarations together, but it doesn't really matter. I left it at the top to help you understand what's going on better.
Hope this helps.
For unlimited event bindings, either use addEventListener or attachEvent method. You cannot add more than one event of the same type using that traditional method.
I don't know if it's a typo in what you put here, but in the loop you are creating a "div" and then assigning it a type of "button". Does that work or is it throwing an error? If it is then that explains why the event handler is not getting the function. Try making it an "input" and see if it now works.
Fixed it!
blacklistitem.innerHTML += blacklist[i];
^ was messing it up, at this point in the code blacklistitem is still a javascript item, not yet appended to its to-be parent element in the document
So I just stuck blacklist[i] into a span tag and appended as a child and now it works fine :)

HTML5,draggable and contentEditable not working together

When a draggable attribute is enabled on a parent element(<li>) I cant make contenteditable work on its child element (<a>).
The focus goes on to child element (<a>),but I cant edit it at all.
Please check this sample
http://jsfiddle.net/pavank/4rdpV/11/
EDIT: I can edit content when I disable draggable on <li>
I came across the same problem today, and found a solution [using jQuery]
$('body').delegate('[contenteditable=true]','focus',function(){
$(this).parents('[draggable=true]')
.attr('data-draggableDisabled',1)
.removeAttr('draggable');
$(this).blur(function(){
$(this).parents('[data-draggableDisabled="1"]')
.attr('draggable','true')
.removeAttr('data-draggableDisabled');
});
});
$('body') can be replaced by anything more specific.
If new contenteditable elements are not added in the runtime, one can use bind instead of delegate.
It makes sense that the draggable and contenteditable properties would collide. contenteditable elements, like any text field, will focus on mousedown (not click). draggable elements operate based on mousemove, but so does selecting text in a contenteditable element, so how would the browser determine whether you are trying to drag the element or select text? Since the properties can't coexist on the same element, it appears that you need a javascript solution.
Try adding these two attributes to your anchor tag:
onfocus="this.parentNode.draggable = false;"
onblur="this.parentNode.draggable = true;"
That works for me if I add it to the <a> tags in your jsFiddle. You could also use jQuery if it's more complicated than getting the parentNode.
Note: This is a workaround since I believe the inability for these two functionalities to work together resides in the HTML spec itself (i.e. the not working together thing is intentional since the browser can't determine whether you want to focus or drag on the mousedown event)
I noticed you explicitly set 'no libraries', so I will provide a raw javascript/HTML5 answer
http://jsfiddle.net/4rdpV/26/
This was my crack at it.
First of all, it might be better to include the data in one single localStorage item, rather than scatter it.
storage={
'1.text':'test 1',
'2.text':'test 2'
}
if(localStorage['test']){
storage=JSON.parse(localStorage['test'])
}
this creates that ability, using JSON to convert between object and string. Objects can indeed be nested
I also added (edit) links next to the items, when clicked, these links will transform the items into input elements, so you can edit the text. After hitting enter, it transforms it back and saves the data. At the same time, the list items remain draggable.
After saving, hit F12 in chrome, find the console, and look in the localStorage object, you will see all the data was saved in localStorage['test'] as an Object using JSON.stringify()
I tried my best to design this to be scaleable, and I think I succeeded well enough; you just need to replace the HTML with a container and use a javascript for loop to write out several items, using the iterator of your choice to fill the parameter for edit(). For example:
Say you changed storage to hold "paradigms" of lists, and you have one called "shopping list". And say the storage object looks something like this:
{
"shopping list":{
1:"Milk",
2:"Eggs",
3:"Bread"
}
}
This could render that list out:
for(i in storage['shopping list']){
_item = storage['shopping list'][i];
container.innerHTML+='<li draggable=true><a id="item'+i+'">'+_item+'</a> (edit)</li>'
}
Of course, if you were to edit the structure of the storage object, you would need to edit the functions as well.
The output would look something like this:
Milk (edit)
Eggs (edit)
Bread (edit)
Don't worry about the input elements if that worries you; CSS can easily fix it to look like it didn't just change.
If you don't want the (edit) links to be visible, for example, you can do this in CSS:
a[href="#"]{
display:none;
}
li[draggable="true"]:hover a[href="#"]{
display:inline;
}
Now the edit links will only appear when you hover the mouse over the list item, like this version:
http://jsfiddle.net/4rdpV/27/
I hope this answer helped.
Using html5sortable and newer JQuery events (delegate is deprecated, answer 3 years after initial question), bug still affects Chrome 37. Contenteditable spans and html5sortable seem to play nice in other browsers. I know this is only partially relevant, just keeping documentation on changes I've noticed.
$(document).on('focus', 'li span[contenteditable]', function() {
$(this).parent().parent().sortable('destroy'); // removes sortable from the whole parent UL
});
$(document).on('blur', 'li span[contenteditable]', function() {
$(this).parent().parent().sortable({ connectWith: '.sortable' }); // re-adds sortable to the parent UL
});

How to detect if an element is not visible in a fast way in JavaScript?

In the past we used the CSS attribute "display" to show and hide DOM elements. To check if an element is visible, we could just use:
element.offsetWidth > 0
Since we had some problems with Flash and Java Applets (they stop when they get display:none) we switched to the CSS attribute "visibility".
I am now looking for a fast and easy way to check if an element is not visible.
I have tried the following:
Checking the attribute itself on the element and and all parents => too slow
Checking the calculated style directly from the browser (element.currentStyle or window.getComputedStyle() plus getPropertyValue(style property)) => also too slow
Do you know any other way or shortcut to see if an element is visible?
use JQuery and the you can do this
var isVisible = $('#foo').is(':visible');
Remember that visibility:hidden makes an element hidden, but that element still occupies its space, which may have some unexpected consequences on the layout (it may be an advantage as well if you are aware of this).
I would use absolute positioning to move the element far to the left, outside possible screen width. This gets the element out of the flow so the hidden element has no impact on layout, makes the element practically invisible, and it doesn't have the disatvantages of display:none.
.hide {
position:absolute;
left:-3000px;
}
Then to determine if an element is hidden you can use its offsetLeft property:
if( myElement.offsetLeft < 0 ){ /* it's hidden */ }
If you need to determine if a child element is off the screen (you don't know if it's the hidden element or its child) you can use .offsetParent and a while loop, as described in PPK's Find Position article.
Toggling Element Visibility by Kent is an unobtrusive, semantically valid way of presenting content that will degrade nicely for non-CSS-aware browsers.
After the page loads completely, we crawl through the entire document tree and look for block-level elements styled with class name toggle. If we find one that says toggle closed, we immediately hide its next sibling element, by styling it with class name hidden.
When we find one, we tell it to listen for mouse clicks.
When one of our pet elements hears a click, it leaps into action, hiding (or showing) its next available sibling, the same way we did it during the initial crawl.
All three class names (toggle, closed, and hidden) are fed in at the bottom in the init call, and may be changed to any valid class name.
Also look at this DevX article which compares the Display and Visibility properties.
Checking the focus would work, either parent is visible or not.
var isVisible = true;
try{
document.getElementById("target").focus();
}catch(err){
isVisible = false;
}
It obviously should work on input or link, but for other element, I'm not sure.
I have studied the same problem before using jQuery, but that time my aim is to focus the first availabe field on a form. The resulting code is like:
$(":text:visible:enabled").filter(function(){
return $(this).parents.filter(function(){
return this.style.display == "none";
}).size()==0;
}).slice(0,1).focus();
It would also work for hidden/invisble parent.
CSS selectors are optimised to find sets of matching elements. There are several libraries implementing this functionality. JQuery, ExtJS Core to name a couple.
Using Ext Core, I could write a javascript function that checks for visibility as follows:
// Checks whether the element is currently visible using
// both visibility and display properties
if(Ext.get(el).isVisible()){
alert('it\'s visible');
};
see http://extjs.com/products/extcore/docs/?class=Ext.Element for more Ext Core Ext.Element functionality.
function isVisible(elem) {
return elem.style.visibility !== "hidden";
}

Categories