HTML elements for JavaScript hooks - javascript

Most of my development life, I have done a JavaScript generated button like so:
<span>Open/hide</span>
With CSS like
span {
cursor: pointer;
}
This seemed to work for me, and IMO, it looks and seems better than
open/hide
However, I'm starting to think people might use links because they are better accessible to screen readers. It also makes sense that the browser will allow you to tab to a link - but not necessarily a span with a CSS style (like my previous example).
Is there a way to get the best of both worlds? That is, a button that can be tabbed to, accessible to screen readers and also not implying a semantic link to another document, and not having to use hacky ways to stop the link from going anywhere?
Note: These are for JS inserted buttons only, not existing links of which their behavior is changed with JS.

That is, a button that can be tabbed to, accessible to screen readers and also not implying a semantic link to another document
Yes, and curiously enough it's called just that: <button>. Or <input type="button"> if you only need text content. You can then add a load of CSS to it to stop it looking like a default form button if you prefer.
The one problem is IE, which adds an extra couple of pixels of unremovable padding to button widgets, and moves the content of the button down and to the right one pixel when clicked. Often this doesn't matter. If you don't need zero padding you could also compensate by adding padding on all browsers except IE.
Another semantically sound possibility is to use an <input type="image">, although that obviously requires the content to be an image. Relying on the alt text instead doesn't work due to IE's rendering of broken image icons.
Whilst buttons are semantically by far the most appropriate element, if you don't want this styling trouble you can continue to use a span or other meaningless element (like SO does), but at least improve the keyboard accessibility by giving it a tabindex value. It will then be focusable like a link, though unfortunately you need extra scripting if you want to make it activatable from keyboard (catch at least keypress codes 13 and 32). You can also label such an element as a button with ARIA for accessibility, athough to be honest I'm not convinced this really does anything much useful for today's toolset.
Sometimes when the action is related to another element on the page, such as with the suggested ‘open/hide’ button, it seems legitimate to me to use an internal link to that element. (With a JS-assigned onclick though, not any of that inline JS or horrific javascript: pseudo-URLs.)
.hidden { display: none; }
open/hide
<div id="foo"> blah </div>
for (var i= document.links.length; i-->0;)
if (document.links[i].className==='toggler')
document.links[i].onclick= toggle;
function toggle() {
var el= document.getElementById(this.hash.substring(1));
el.className= el.className==='hidden'? 'hidden' : '';
}
or similar idiom in your library of choice. This will do something useful in non-JavaScript browsers; you can also add a rule to check the current location's hash, so that if someone opens your show-link in a new tab that document will be showing the element in question.

It seems doing
<span tabIndex="n">foo</span>
allows you to tab it. However, pressing enter while it's focused doesn't simulate clicking it. Which I guess is easily remedied by putting a keydown event on it as well.

I would say that the button fits your requirements ...
<input type="button" class="js_button" value="Javascript Button" />
CSS:
.js_button, .js_button:active, js_button:focus {
background-color: #FFF;
border: none;
margin: 0 0;
padding: 0 0;
}
Buttons can be tabbed to, are semantic, automatically have tab indexes and keypress actions, and do not imply links to other documents. The only difficulty is removing the mousedown animation -- I'll look around and see if there's anything out there for removing that animation without hacks.

You can link to any object in the page if you provide the id or the name of the element.
For example:
https://stackoverflow.com/questions/1659137/html-elements-for-javascript-hooks#comments-1659137
HTML elements for JavaScript hooks

Related

Toggling an html IMG element's class using JS (no Jquery)

I give up... All of your answers were just different ways of targeting the local element.
If you bothered to actually read what I was saying you would realise that it was not a problem with the code I already had, just that the code DID NOT work on IMG tags.
While faffing around trying to demonstrate my problem (and that none of your solutions did anything different to what was already happening) I found that I can achieve exactly what I want by applying a Grayscale filter to a DIV element placed over each image. The mouseover event then triggers an opacity change in the DIV element.
It is a little heavier that I wanted but it answered my ACTUAL question. The answer being:
Yes, there probably is a way to toggle class of IMG tags. But no, I am probably not going to find it here without causing arguments or being told i'm using "bad code". So yes, it IS easier and more efficient to target DIV elements.
By the way, page load times are about how large data packages are. Larger data packages (images, html/css/js documents, etc) take longer to download and so the page takes longer to load. The website I am trying to create proves this thesis, I have an almost complete and (almost) fully functional website with loads of 'clever' little effects all under 20mb, about 15mb of which is images. This website is clean and simple, is hosted on my Samsung Galaxy Tab 4 (using Papaya) and loads almost instantly.
THIS is what I meant by "I want this to be VERY lite". Thank you all for your attempts to help, it's just a shame that I couldn't get anyone to understand what was going on.
If you add onClick to image element you don't need to pass anything, you will receive MouseEvent which contains all information. You need target from event.
I suggest to not use onClick on element as it is not scalable, you have to add it to all elements. Better to add listener to wrapping/container element and then filter target by some attribute e.g data-something Please check fiddle
So you have wrapping element and you images:
<div class="images-container">
<img src="https://placeholdit.imgix.net/~text?txtsize=33&txt=350%C3%97150&w=350&h=150" data-toggleable class="thumb-gray thumb-color" />
<img src="https://placeholdit.imgix.net/~text?txtsize=33&txt=350%C3%97150&w=350&h=150" data-toggleable class="thumb-gray" />
<img src="https://placeholdit.imgix.net/~text?txtsize=33&txt=350%C3%97150&w=350&h=150" data-toggleable class="thumb-gray" />
</div>
and you attach listener to you wrapping element. It is best practice as you don't attach listeners to each element and same time you are able easily scale your solution
var imagesContainerEl = document.querySelector('.images-container');
imagesContainerEl.addEventListener('click', function(event) {
var element = event.target;
if (element.hasAttribute('data-toggleable')) {
element.classList.toggle('thumb-color');
}
});
The same code can be extended to support mouseover and mouseout. Check fiddle2. One function to rule them all and in the darkness bind them..
var imagesContainerEl = document.querySelector('.images-container');
imagesContainerEl.addEventListener('mouseover', onToggleImage);
imagesContainerEl.addEventListener('mouseout', onToggleImage);
function onToggleImage(event) {
var element = event.target;
if (element.hasAttribute('data-toggleable')) {
element.classList.toggle('thumb-color');
}
}
Also updated fiddle which shows how to make image grayscale/color
Is what you refer to in your question as
onClick="colorFunction(image1)"
an inline javascript event listener?
If so, try replacing it with:
onClick="colorFunction(this)"
and rewrite colorFunction() as:
function colorFunction(image) {
image.classList.toggle('thumb-color');
}

On IE11 Windows 8.1, button remains highlighted after clicking

I'm working on a (relatively) complex web application, and encountering a very specific issue. On the specific OS and Browser mentioned in the title, in one of the forms that shows up, when some buttons are clicked, they remain "highlighted" as if the mouse were still hovering over them, even when the mouse leaves the button element or if the user clicks on a separate element on the page. It looks like this:
(the "First Name", "Last Name", etc. elements are the buttons)
The specific behavior of these buttons is that when they are clicked, they put a specific bit of text into the text form shown, then focus the text form. This is done using some standard jQuery functions (.val(), .focus(), etc.) On any OS/browser pair I have seen other than Win8.1/IE11, this does not happen. (it doesn't happen on Win7/IE11 even!) And as far as I can tell, there are no other handlers attached to the buttons than click, and the only styles that seem to be applied have to do with size, color, and text.
Honestly, I'm completely stumped about this behavior. I've even tried doing things as making sure to call .blur() on the button, trigger mouseout events, etc. and nothing seems to make a dent in this. The only thing that seems to cause the highlighting to go away is to click very close to the buttons, but not on them, or to drag-select some text.
EDIT December 7, 2015: This has been on my backburner for a while with other more-pressing issues to deal with. But I'm going to do my best to provide a nice update with more information so that this can hopefully be resolved. (assuming this isn't some browser bug in the specific version of IE on Windows 8.1)
I have verified that the only event handler attached to the button is an onclick handler defined in the HTML itself by checking what IE11's dev tools see as the event handlers on the element:
<input type="button" value="${someValue}" onclick="autoText('${token}');"/>
autotext is a very simple function that does what is described above, but here is the actual source code for it:
function autoText(token) {
if(null != lastFocus) {
var str = lastFocus.val(),
tokenStr = '$\{' + token + '\}',
newStr = str.substring(0, pos) +
tokenStr + str.substring(pos, str.length);
lastFocus.val(newStr);
var lastPos = pos + tokenStr.length;
lastFocus.focus();
lastFocus.caret(lastPos);
lastFocus.keypress();
}
where lastFocus is set to the text input element that was last focused and pos is set to where the caret is in the text input element.
The button that has the onclick listener is in the following hierarchy, as given to me by Chrome: html -> body -> div -> div#templateFormDiv -> form#prefsEmailTemplatesSave -> table -> tbody -> tr -> td -> input (where -> indicates it is the parent)
html has the classes mod-js mod-canvas mod-no-touch mod-postmessage mod-contenteditable mod-cookies mod-filereader mod-formvalidation mod-fileinput mod-formattribute mod-getusermedia
body has no CSS classes
div has ui-dialog ui-widget ui-widget-content ui-corner-all ui-front ui-dialog-buttons ui-draggable ui-resizable
div#templateFormDiv has the classes hidden preferencesDialog ui-dialog-content ui-widget-content
form#prefsEmailTemplatesSave has the class savePrefsForm
table, tbody, tr, td, and input don't have any classes associated with them.
The CSS rules for all the listed classes, ids, and tags are as follows:
*{padding: 0; margin: 0;}
body {
margin:0;
padding:0;
height:100%;
font-family:'Lucida Grande',Verdana,Arial,Sans-Serif;
min-height: 100%;
font-size: 62.5%;
color: #333;
}
form {
margin:0;
padding:0;
}
.hidden {
display: none;
}
All other classes, as far as I can tell, are included as part of bits of jQuery or are there as markers for DOM queries. (e.g.: savePrefsForm)
div#templateFormDiv has a dialog attached to it via a jQuery dialog() call.
form.savePrefsForm is selected and all found elements have an ajaxForm attached to them via jQuery. In a similar vein, #prefsEmailTemplatesSave is selected and submitted if the #templateFormDiv dialog has its save button pressed and it passes some validation logic.
The last thing I can think of to note here is that there is a TinyMCE editable text area next to where the buttons are. No parent/child relationship, but rather a sibling one.
I'll try to think of any further details that matter and include them if I do, otherwise I'll just answer any further questions that anyone has about what is going on.
Have same issue on IE 9-11 Windows 8.1-10. Every button that not cause postback, remains highlighted after click. On the other sites buttons work properly, so it was clearly my issue. I found it because CSS selector "hover" in my styles:
.navigationLink:hover {
}
I have changed it to:
a.navigationLink:hover {
}
and now buttons highlighting works properly. I can't understand what kind of magic is it. Any help would be appreciated.

Prevent Chrome from wrapping contents of joined <p> with a <span>

I have observed an undesirable behaviour in Chrome that occurs when one joins two <p>'s by deleting the separation between them. Although the <p> tags are joined properly, Chrome wraps the right-most <p> tag's content with a <span>.
Edit: this happens for all block elements, not just p tags.
Example:
For example, when the separating </p><p> are deleted from the following block:
<div contenteditable="true"><p>p one.</p><p>p two.</p></div>
It becomes:
<div contenteditable="true"><p>p one.<span style="font-size: 16px; line-height: 1.44;">p two.</span></p>
Example in a fiddle: Chrome wrapping contents of joined <p> with a <span>.
Question:
Is there an easy way to prevent chrome from doing this? It results in horrible markup that I'd like very much to be rid of.
There is a way but you need to pro-actively set a few styles. The idea is to tell Chrome that the styles are already taken care of, so it doesn't need to add SPAN to meet the styles requirement.
basically, you need to add the chrome added styles to a span class under your contenteditable div ( see example below).
Edited fiddle
For you example:
I added an "edit" class to the contenteditable DIV
I added an .edit p, span class in the style
This becomes:
.edit {
border: 1px solid gray;
padding: 10px;
}
.edit p, span {
line-height: 1.44; font-size: 16px;
}
And the DIV:
<div contenteditable="true" class="edit">...</div>
Note that you normally don't need the font-size: 16px;. I needed to add this one because fiddle defines some font size in contenteditable. On a standalone page I didn't need it.
You need to apply this Chrome 'patch' to any elements where it happens (so if you need UL, OL... then add what is needed following my example logic above)
I know it is not really an answer to solve it, but a hint how it could be fixed (but it is to long to be a comment to Petah question how i would solve it)
in general you would check when such bugs could happen. for the case of the span creation you would listen to all keydown and keypress events and check if the key is the backspace/delete key or for every key that inserts chars if it is a real selection.
if this is the case then you need to check the current selection (either the position of the insert mark, or the real selection) then you know which is the next following text-element or node. then you need to check the in the next following keypress and keyup if there is a span created directly after your insert mark. depending on the browser bug you need some further checking. if there is one create unwrap its content again. additionale Mutation events and helper attributes could be used.
But i need to say that i gave up in doing this myself and switched over to ckeditor 4. most of the it's features i don't need and it is a really big library. but cause of the huge number of such bugs i did not see another solution for me.
EDIT
Here an update of the js fiddle that shows the idea with a Mutable event:
http://jsfiddle.net/THPmr/6/
But that is not bullet proofed, it is just to show how it could be achived ( only tested in chrome 27.0.1422.0, and probably would not work if more then one text element is contained in the second p )
Here is my take on removing the extra spans
document.querySelector('[contenteditable=true]')
.addEventListener('DOMNodeInserted', function(event) {
if (event.target.tagName == 'SPAN') {
event.target.outerHTML = event.target.innerHTML;
}
});
The CSS is influencing how the markup is made inside contenteditable:
div, pre {
border: 1px solid gray;
padding: 10px;
line-height: 1.44;
}
Delete the line-height line and the problem doesn't occur any more.
There are in general several bugs with contenteditable related to default styling : How to avoid WebKit contentEditable copy-paste resulting in unwanted CSS?
EDIT JsFiddle IS indirectly influencing this (tinkerbin behaves differently) because of its' CSS (normalize.css). Try this:
Run your fiddle
Inspect a <p>
Disable all font-size declarations in the CSS stack - including your line-height
do the backspace
there is no inline span
Solution 1 : Use classes and id's.
Don't declare font-size for p or div but for p.main-content, or more simply, .main-content.
If the font-size of your elements inside contenteditable is coming from the browsers' internal default CSS then Chrome won't add extra markup/inline styling.
Solution 2 : Use a Sanitizer.
I'd personally go with #1 as it's never a good practice to specify font-sizes and typo in so generic tags.
The best way I found so far is to listen to DOMNodeInserted and check the tagName. If it is a span, you can remove the tag and but leave the contents. This also keeps the cursor at the correct place.
function unwrap(el, target) {
if ( !target ) {
target = el.parentNode;
}
while (el.firstChild) {
target.appendChild(el.firstChild);
}
el.parentNode.removeChild(el);
}
var AutoFix = true;
document.getElementById('editable')
.addEventListener('DOMNodeInserted', function(ev) {
if ( !AutoFix ) {
return;
}
if ( ev.target.tagName=='SPAN' ) {
unwrap(ev.target);
}
});
I've added a boolean 'AutoFix' so you can disable the automatic dom changes when you do need to insert a span, since this event fires on any dom change. E.g. if you have a toolbar that allows the user to insert something like <span class="highlight">...</span>.
The code has no side effects in IE or FireFox as far as I can see.
This irritated me as well, but I found a solution that works well enough for now.
$('#container span').filter(function() {
$(this).removeAttr("style");
return $(this).html().trim().length == 0;
}).remove();
I simply remove the style tag from the span element and remove it altogether if it's empty. You could probably filter based on the attribute style, but as I'm already doing a loop to check to remove empty spans, I thought it was best to do both at the same time.
It does create a flicker for a microsecond when chrome first tries to insert the style inherited for the span, but you only see that once immediately after deletion.
It isn't perfect, but it's quick and concise.
Found this link from a comment posted on someone's blog:
(Fixed bug where the latest WebKit versions would produce span element…)
https://github.com/tinymce/tinymce/commit/8e6422aefa9b6cc526a218559eaf036f1d2868cf
Please see the answers here: https://stackoverflow.com/a/24494280/2615633
To fix the problem you may just use this plugin: jquery.chromeinsertfix
After several attempts of using provided solutions, I came up with this script:
var content = obj.textContent;
obj.innerHTML = '';
obj.textContent = content;
When someone pastes a text with html encoded chars, putting it to innerHTML results with a valid html tags, that is why I decided to purge innerHTML content before placing content into obj

How do I change the CSS of child of an a-element

I'm getting a bit confused with my work.
My Problem is, I want to change a Element that's covered with an Element.
the HTML-structure:
<ul class="calendar-content">
<li>
<a href="#day02" onClick="popup('02');">
<span class="day"><strong>02</strong></span>
<span class="content" id="content">
<small>02</small>
<strong>Inhalt Tag 01</strong>
</span>
</a>
</li>
The user is only seeing the first span(class=day). After a click on the link the
second span(class=content) should "appear".
My first idea was:
.calender-content .a:visited .content{
display:block;
}
Or this one:
//.calender .content got margin-left:120px and is out of view
.calender-content .a:visited .content{
margin-left:0px;
}
But nothing happens. Maybe it isn't valid but I saw stuff like this before.
I just want to display the second span after the link is visited.
Either setting the display style to block or changing the margin to 0 and animate that with
-transition.
But nothing the styles doesn't appear on the span element.
If there's a way arround in CSS, that would be great. So I don't have to use JS.
Cheers Dan
Should be:
.calendar-content a:visited .content { display: block; }
You have ".a", which means "elements with class 'a'", not "elements with tag name 'a'" :-)
Now the thing is, I'm not sure that ":visited" will be "true" (or whatever the appropriate term would be) if your <a> tag isn't really something that "visits" another URL. If that's the case, then your event handler can add a class to the anchor. If your event handler does allow the anchor to move to the label, then I think it should work.
Your first problem is the . notation that means "class" and not "tag". See #Pointy's answer for details and fix.
But then, you shouldn't rely on :visited. Indeed, it can be used for history sniffing, and browsers will probably end up removing support for most CSS properties that change layout (such as display), just like Firefox already does.
A pure CSS solution would be to use the :target pseudo-class. However, it is not supported in IE ≤ 8. If it is a requirement, you should go to JS.
Any way you choose, you should refactor your markup. The link should not contain something it toggles. <small> is a good example of an awful markup-as-presentation use. Use a classed span.

jQuery Graceful Degradation

I want to spruce up some areas of my website with a few jQuery animations here and there, and I'm looking to replace my AJAX code entirely since my existing code is having some cross-browser compatibility issues. However, since jQuery is a JavaScript library, I'm worried about my pages not functioning correctly when JavaScript is turned off or doesn't exist in a user's browser.
I'll give an example: Currently, I'm using a pure CSS tooltip to give my users (players, the site is a browser game) information on other users. For example, if the other players in the game satisfy one or more conditions, a target icon is displayed next to their name, and upon hovering over that target icon information regarding the reasons behind the target is displayed. This is useful information, as it helps my players to know who they should plan to attack next in the game.
Currently, I do such tooltips using CSS. I have a parent div that holds the image of the target icon of class "info". I then have a div inside of that with class "tooltip" that, on the hover state of the "info" class that it is contained in, is shown, but on the normal state is hidden. I thought it was rather clever when I read about it, and since no JavaScript is used it works on any CSS compliant browser.
I would like to use jQuery to achieve the same effect, mostly because it would look much cleaner, but also because I believe quick and subtle animations can make such things "randomly appearing" make a lot more sense to the user, especially on the first encounter. I'm just wondering if the two will conflict. This is only one example of this, there are numerous other examples where the inability to use JavaScript would hinder the site.
So what I'm asking I guess is, how does one make a jQuery site degrade gracefully on browsers that do not support JavaScript, but otherwise do support most CSS? My goal is for the site to function on a basic level for all users, regardless of choice in browser. The animation is a good example, but I'm also worried about the more dynamic bits, like the auto-updating with AJAX, etc. Are there any good resources on how to achieve this, or do you have any advice about the best way such degradability could be achieved?
Thanks
PS: Totally irrelevant, but Firefox seems to think that "degradability" isn't a word, but "biodegradability" (with the "bio" prefix) is. Weird...
If you consider the "Cascading Order" of css, could you not just add a css style at the very end of all your previous css definition in order to cancel any css effect you currently have for tooltip effect ?
That css rule would only be declared if Javascript is activated and JQuery detected.
That way, you are sure your css tooltip effect is not in conflict with your JQuery effect.
Something like:
a.info:hover span{ display:none}
with the use of "js_enabled" class to make this css rule conditional.
You also can do it by adding css rule on the fly:
function createCSSRule(rule,attributes)
{
//Create the CSS rule
var newRule = "\n"+rule+"{\n";
for (var attribute in attributes)
{
newRule += "\t" + attribute + ": " + attributes[attribute] + ";\n";
}
newRule += "}\n";
//Inject it in the style element or create a new one if it doesn't exist
styleTag = $E('style[type="text/css"]') || new Element("style").setProperty('type','text/css').injectInside(document.head);
if(window.ie)
{
styleTag.styleSheet.cssText += newRule;
}
else
{
styleTag.appendText(newRule);
}
}
The most simple solution for Separation of CSS and Javascrip is to remove your css class
function jscss(a,o,c1,c2)
{
switch (a){
case 'swap':
o.className=!jscss('check',o,c1)?o.className.replace(c2,c1): <-
o.className.replace(c1,c2);
break;
case 'add':
if(!jscss('check',o,c1)){o.className+=o.className?' '+c1:c1;}
break;
case 'remove':
var rep=o.className.match(' '+c1)?' '+c1:c1;
o.className=o.className.replace(rep,'');
break;
case 'check':
return new RegExp('\\b'+c1+'\\b').test(o.className)
break;
}
}
This example function takes four parameters:
a
defines the action you want the function to perform.
o
the object in question.
c1
the name of the first class
c2
the name of the second class
Possible actions are:
swap
replaces class c1 with class c2 in object o.
add
adds class c1 to the object o.
remove
removes class c1 from the object o.
check
test if class c1 is already applied to object o and returns true or false.
If something can be done completely in CSS I say keep it that way. If lack of javascript in the browser is a concern, then most of the time I show the entire page unaffected.
Say for instance I'm going to use jQuery to toggle an element when a checkbox is clicked. On page load I look at the checkbox and update the element accordingly. If javascript is not enabled the element will still appear and the site will still be usable. Just not as nice.
Man, you have a browser-based game, right? You have less than 1% users with JS disabled! And that 1% is the apocalyptic number because I can BET that you have less than that ;)
Anyhow, if you are really concerned about this, just do the site without any JavaScript. And make it functional 100%. After your site works completely without any JS flavour, just start to improve with jQuery (or any other library; jQuery is the best :P ). But with careful: do not change ANY of you HTML. It's easier than it looks ;)
And yes, if you have things that work without JS (like those tooltips) keep it!

Categories

Resources