UPDATE: After having the code out in the wild for a while, I found two issues on Citrix and iOS Safari. Both seem to be around the use of Eval. The citrix issue could have been resolved by updating the CSS to :
emailerrormessage = 'please enable Javascript'; ahref.attr('data-edomain') + '\0040' + ahref.attr('data-ename');
The iOS Safari issue was not something I managed to resolve. I ended up cutting out the CSS element altogether.
EDIT: I'm opening up this question for recommendations on why my solution might be considered bad programming in general, and if anyone else has a better way of obfuscating emails through a combination of CSS, HTML and JS. I've added an answer of my own findings, but haven't marked it as the answer in case someone else might have better insight into this technique.
I've been tasked to obfuscate email addresses on some webpages, and after encountering an answer suggesting use of CSS and data attributes I tried implementing it myself and found that getting the produced email address back into a mailto element was impossible without some JavaScript. The next problem encountered was that the JavaScript I ended up using to grab the rendered email address was not cross-browser as some browsers retrieve "attr(data-xx)" instead of the actual value. However I still like the idea of producing a solution that spans HTML, JS and CSS for maximum complexity. The last resort was to store a line of JS in the CSS content property, and use eval to produce the final email address.
Obfuscation is not supposed to be pretty, but I want to know if what I've done is potentially compromising security or performance by introducing eval() and/or storing JS in CSS. I haven't found another example of someone doing something similar (maybe for good reason).
My HTML is
<a class="redlinktext ninjemail" data-ename="snoitagitsevni" data-edomain="ua.moc.em" data-elinktext="click me"></a>
My CSS is
.ninjemail:before {
content: "'please enable Javascript'; ahref.attr('data-edomain') + '\0040' + ahref.attr('data-ename');"
}
My JavaScript is
$('.ninjemail').each(function () {
var fullLink = "ma";
var ahref = $(this);
fullLink += "ilto" + ":";
var codeLine = window.getComputedStyle(this, ':before').content.replace(/\"|\\/g, '');
var emailAddress = eval(codeLine);
emailAddress = emailAddress.split('').reverse().join('');
fullLink += emailAddress;
ahref.attr('href', fullLink);
var linkText = ahref.attr('data-elinktext');
if (linkText && linkText.length > 0) {
ahref.text(linkText);
} else {
ahref.text(emailAddress);
}
ahref.removeClass('ninjemail');
});
Based on the research I've done so far the only real drawback from using this method for obfuscation is that in teams where there is a clear separation of roles, Javascript in CSS files may confuse layout designers, and vice versa (programmers won't want to have to edit CSS files).
Performance wise there will always be more overhead in processing the email address across the three technologies. I can't say I'm an expert on performance testing, but I did a page refresh timing of before and after and got a DOMContentLoaded time of .956s vs .766s without obfuscation. Load time was 1.22s vs 1.17s without obfuscation. Not a concern for my own situation, but may be an issue on more intensive applications. Note that this was a once off test, and not an average of multiple runs in a controlled environment.
I have a plugin for inserting tag <cut /> in text.
It works fine, result is expectable, but in editor window <cut /> transforms into <cut></cut>, wraps the paragraphs below and hampers further edits.
GIF - http://gyazo.com/dd7c36ba7cb7bc7cb00186cfb83e5fbc
Any ideas how to fix it?
CKEDITOR.plugins.add('pagecut', {
lang: 'de,en,ru',
onLoad: function(){
var css = ('display:block;clear:both;width:100%;border-top:#999 1px dotted;padding:0;height:1px;cursor:default;');
var cssBefore = (
'content:"";' +
'background: url(' + CKEDITOR.getUrl( this.path + 'images/image.png' ) + ') no-repeat right center;' +
'height:14px;width:25px;position:relative;display:block;top:-8px;float:right;'
);
CKEDITOR.addCss( 'cut{' + css + '} cut:before{' + cssBefore + '}' );
},
init: function(editor) {
CKEDITOR.dtd['cut'] = {};
CKEDITOR.dtd.$empty['cut'] = 1;
CKEDITOR.dtd.$nonEditable['cut'] = 1;
CKEDITOR.dtd.$object['cut'] = 1;
editor.addCommand('insertPagecut', {
exec: function(editor) {
var element = CKEDITOR.dom.element.createFromHtml('<cut />');
editor.insertElement(element);
}
});
editor.ui.addButton('Pagecut', {
label: editor.lang.pagecut.toolbar,
command: 'insertPagecut',
icon: this.path + 'images/icon.png',
toolbar: 'links'
});
}
});
Uh, I'm sure that I explained this thoroughly in some question, but I can't find it, so here goes another explanation :D.
There are two important facts that one must understand before trying to edit non-HTML tags in CKEditor:
CKEditor is an HTML editor.
Of course custom tags start to be more and more popular in HTML. You can also say that XML is some kind of generalisation of HTML (although not precisely, because it has other rules), so if CKEditor handles HTML why doesn't it handle other tags equally well. Well - the answer is simple - because HTML tags have a meaning and CKEditor knows it. But it does not know meaning of your custom tags. And the meaning of tags (what a list is, that it has items, that they are block elements, etc.) is crucial to implement editing algorithms.
Fair enough, you could say. But why wasn't CKEditor's configuration (e.g. the CKEDITOR.dtd object) generalised so meaning of every possible tag can be configured? Because every generalisation increases complexity and HTML editing is already complex enough.
So why does the CKEDITOR.dtd object exist at all? Because some components of CKEditor are configurable to some extent. The DTD has the biggest impact on CKEditor's HTML parser (which is used mostly during data processing) so this is the most configurable component. Other algorithms, like the enter key handling, backspace/delete, lists editing (which is a very complex task) are only slightly configurable and there is no guarantee given that they will work with your custom tags.
The editing happens in the browsers and is partially handled by the browsers.
This fact is important because it means that browsers' capabilities are affecting CKEditor's limits too. Browser must be able to parse and render your tags (fortunately, this part works rather well in modern browsers - IE8 is the last one with a huge problems) and must be able to edit it. This means - render caret, handle selection, handle backspace, enter, etc. Since browsers are not easily extensible and their implementations of contentEditable are highly inconsistent, incompatible and buggy, from release to release CKEditor overrides more and more of their native behaviours. Not all yet (actually - it will never override all, because that could be disastrous for certain reasons), but a significant amount. For instance, all the enter key behaviour is custom, on Webkit and Blink CKEditor handles backspace and delete in many scenarios due to still unresolved bugs (1 and 2), it implements its own undo system, intercepts pasted and dropped content and perform custom HTML insertion (I remember that when we implemented it this closed a huge number of tickets), etc., etc.
One of the greatest efforts to ensure consistent, configurable and powerful editing experience is the widgets system. It is full of hacks inside, but it exposes a clean and pretty powerful API to the developer and a very consistent behaviour to the end user. It allows to implement a "special rich content units that are groups of elements which are treated as a single entity inside the editor". So the widgets system has the power to encapsulate part of your content and isolate it from the browsers.
After this short introduction I can finally answer your question. You need to implement your <cut> tag as a widget. You already configured your DTD pretty well (you only forgot to set in what elements the <cut> element can exist and whether it is more like a block or inline element), so the parser will accept it and handle as an empty tag. Now you need to wrap it with a widget in order to isolate it so it does not break the editing experience. That should do the trick.
I use a variable.less config file to store all relevant information about a design.
One of the information is the breakpoint between mobile layout and PC layout.
I also need this information in javascript, and I didn't know how to get it (without storing it in a data-attribute, because I wished to keep all design variables in the same file).
So I finally found that :
I store my break point in the variables.less :
#bk-point: "500px";
I use the css property "content", but not on a pseudo-element, but on any tag (less file):
#any-div {
content: "#{bk-point}";
}
Like this, this doesn't affect the design ("content" property doesn't show on element, only on pseudo-element), and I can get it very easily with jQuery :
var bkPoint = $('#any-div').css('content');
And all my variables are in the less files.
This is perfect for what I want, but is there any side-effect that I don't see ? Is this a bad practice for reasons I cannot imagine ?
Thanks for your advices !
Sébastien.
PS:
1. works in firefox 21.0 and in chromium 27.0
2. and of course, if you've got a better solution …
The css 'content' property is only valid on pseudo elements:
https://developer.mozilla.org/en-US/docs/Web/CSS/content
As much as this seems like a cool idea, I wouldn't feel comfortable using it in production. I think that you should accept that your js variables and css variables will be in 2 different files and then just apply the values via data-attributes.
However, if you really want a creative way to do this only from the css files that can print to the html and thus interact with the javascript, what about using valid properties that will not affect the design?
All you are really doing is storing a string in the html, so you could use a rather obscure element such as counter-reset, and then grab the value via jquery.css()
variables.less: #bkpoint = 500;
css:
#any-div {
counter-reset: break-point #bkpoint;
}
jquery:
$('#any-div').css('counter-reset'); // returns: 'break-point 500'
simple regex function to get you rid of the 'break-point' part of the returned string and you've got what you're looking for.
btw, can do this on any other css property not in use, such as:
border-width: #bkpoint;
border: none;
specs on counter-reset in case you're interested:
http://www.w3.org/TR/CSS2/generate.html#propdef-counter-reset
I heared that simply to say document.getElementById('divId').innerHTML = ""; is not safe for future divId contents editing via JS so how to clean up divs contents safely?
What should always work is:
var div = document.getElementById('divId');
while(div.hasChildNodes()) {
div.removeChild(div.firstChild);
}
innerHTML is not part of any specification but is widely supported by browsers.
I wrote something similar to Felix Kling, only diffirence is that I wanted to pass the id or the reference. So I wrote it like this:
function removeAllChildNodes(node) {
i = (typeof(node) == "object") ? node : document.getElementById(node);
while (i.hasChildNodes()) {
i.removeChild(i.firstChild);
}
}
Setting the innerHTML attribute to nothing will eliminate any DOM nodes withing the specified DIV. There is no risk of "safety".
You can do this safely :P
I believe this is where JS frameworks, such as jQuery, come to light... they are optimized for cross-browser security - which includes secure DOM element removal, although you will loose a bit of computing speed (setting innerHTML is always the fastest way)
as for others wondering about security of this, some browsers (notably MSIE) have flaws that would crash the browser or allow malicious procedures to be executed on client's side under certain conditions, so that's why setting innerHTML might not be as secure as people use to think ;-)
Recently I have been reading more and more about people using custom attributes in their HTML tags, mainly for the purpose of embedding some extra bits of data for use in javascript code.
I was hoping to gather some feedback on whether or not using custom attributes is a good practice, and also what some alternatives are.
It seems like it can really simplify both server side and client side code, but it also isn't W3C compliant.
Should we be making use of custom HTML attributes in our web apps? Why or why not?
For those who think custom attributes are a good thing: what are some things to keep in mind when using them?
For those who think custom attributes are bad thing: what alternatives do you use to accomplish something similar?
Update: I'm mostly interested in the reasoning behind the various methods, as well as points as to why one method is better than another. I think we can all come up with 4-5 different ways to accomplish the same thing. (hidden elements, inline scripts, extra classes, parsing info from ids, etc).
Update 2: It seems that the HTML 5 data- attribute feature has a lot of support here (and I tend to agree, it looks like a solid option). So far I haven't seen much in the way of rebuttals for this suggestion. Are there any issues/pitfalls to worry about using this approach? Or is it simply a 'harmless' invalidation of the current W3C specs?
HTML 5 explicitly allows custom attributes that begin with data. So, for example, <p data-date-changed="Jan 24 5:23 p.m.">Hello</p> is valid. Since it's officially supported by a standard, I think this is the best option for custom attributes. And it doesn't require you to overload other attributes with hacks, so your HTML can stay semantic.
Source: http://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes
Here's a technique I've been using recently:
<div id="someelement">
<!-- {
someRandomData: {a:1,b:2},
someString: "Foo"
} -->
<div>... other regular content...</div>
</div>
The comment-object ties to the parent element (i.e. #someelement).
Here's the parser: http://pastie.org/511358
To get the data for any particular element simply call parseData with a reference to that element passed as the only argument:
var myElem = document.getElementById('someelement');
var data = parseData( myElem );
data.someRandomData.a; // <= Access the object staight away
It can be more succinct than that:
<li id="foo">
<!--{specialID:245}-->
... content ...
</li>
Access it:
parseData( document.getElementById('foo') ).specialID; // <= 245
The only disadvantage of using this is that it cannot be used with self-closing elements (e.g. <img/>), since the comments must be within the element to be considered as that element's data.
EDIT:
Notable benefits of this technique:
Easy to implement
Does not invalidate HTML/XHTML
Easy to use/understand (basic JSON notation)
Unobtrusive and semantically cleaner than most alternatives
Here's the parser code (copied from the http://pastie.org/511358 hyperlink above, in case it ever becomes unavailable on pastie.org):
var parseData = (function(){
var getAllComments = function(context) {
var ret = [],
node = context.firstChild;
if (!node) { return ret; }
do {
if (node.nodeType === 8) {
ret[ret.length] = node;
}
if (node.nodeType === 1) {
ret = ret.concat( getAllComments(node) );
}
} while( node = node.nextSibling );
return ret;
},
cache = [0],
expando = 'data' + +new Date(),
data = function(node) {
var cacheIndex = node[expando],
nextCacheIndex = cache.length;
if(!cacheIndex) {
cacheIndex = node[expando] = nextCacheIndex;
cache[cacheIndex] = {};
}
return cache[cacheIndex];
};
return function(context) {
context = context || document.documentElement;
if ( data(context) && data(context).commentJSON ) {
return data(context).commentJSON;
}
var comments = getAllComments(context),
len = comments.length,
comment, cData;
while (len--) {
comment = comments[len];
cData = comment.data.replace(/\n|\r\n/g, '');
if ( /^\s*?\{.+\}\s*?$/.test(cData) ) {
try {
data(comment.parentNode).commentJSON =
(new Function('return ' + cData + ';'))();
} catch(e) {}
}
}
return data(context).commentJSON || true;
};
})();
You can create any attribute if you specify a schema for your page.
For example:
Addthis
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:addthis="http://www.addthis.com/help/api-spec">
...
<a addthis:title="" addthis:url="" ...>
Facebook (even tags)
<html xmlns:og="http://opengraphprotocol.org/schema/" xmlns:fb="http://www.facebook.com/2008/fbml">
...
<fb:like href="http://developers.facebook.com/" width="450" height="80"/>
The easiest way to avoid use of custom attributes is to use existing attributes.
use meaningful, relevant class names.
For example, do something like: type='book' and type='cd',
to represent books and cds. Classes are much better for representing what something IS.
e.g. class='book'
I have used custom attributes in the past, but honestly, there really isn't a need to for them if you make use of existing attributes in a semantically meaningful way.
To give a more concrete example, let's say you have a site giving links to different kinds of stores. You could use the following:
<a href='wherever.html' id='bookstore12' class='book store'>Molly's books</a>
<a href='whereverelse.html' id='cdstore3' class='cd store'>James' Music</a>
css styling could use classes like:
.store { }
.cd.store { }
.book.store { }
In the above example we see that both are links to stores (as opposed to the other unrelated links on the site) and one is a cd store, and the other is a book store.
Embed the data in the dom and use metadata for jQuery.
All the good plug-ins support the metadata plugin(allowing per tag options).
It also allows infinitely complex data/data structures, as well as key-value pairs.
<li class="someclass {'some': 'random,'json':'data'} anotherclass">...</li>
OR
<li class="someclass" data="{'some':'random', 'json': 'data'}">...</li>
OR
<li class="someclass"><script type="data">{"some":"random","json":"data"}</script> ...</li>
Then get the data like so:
var data = $('li.someclass').metadata();
if ( data.some && data.some == 'random' )
alert('It Worked!');
I see no problem in using existing XHTML features without breaking anything or extending your namespace. Let's take a look at a small example:
<div id="some_content">
<p>Hi!</p>
</div>
How to add additional information to some_content without additional attributes? What about adding another tag like the following?
<div id="some_content">
<div id="some_content_extended" class="hidden"><p>Some alternative content.</p></div>
<p>Hi!</p>
</div>
It keeps the relation via a well defined id/extension "_extended" of your choice and by its position in the hierarchy. I often use this approach together with jQuery and without actually using Ajax like techniques.
Nay. Try something like this instead:
<div id="foo"/>
<script type="text/javascript">
document.getElementById('foo').myProperty = 'W00 H00! I can add JS properties to DOM nodes without using custom attributes!';
</script>
I'm not doing using custom attributes, because I'm outputing XHTML, because I want the data to be machine-readable by 3rd-party software (although, I could extend the XHTML schema if I wanted to).
As an alternative to custom attributes, mostly I'm finding the id and class attributes (e.g. as mentioned in other answers) sufficient.
Also, consider this:
If the extra data is to be human-readable as well as machine-readable, then it needs to be encoded using (visible) HTML tags and text instead of as custom attributes.
If it doesn't need to be human readable, then perhaps it can be encoded using invisible HTML tags and text.
Some people make an exception: they allow custom attributes, added to the DOM by Javascript on the client side at run-time. They reckon this is OK: because the custom attributes are only added to the DOM at run-time, the HTML contains no custom attributes.
We've made a web-based editor that understands a subset of HTML - a very strict subset (that understood nearly universally by mail clients). We need to express things like <td width="#INSWIDTH_42#"> in the database, but we can't have that in the DOM, otherwise the browser where the editor runs, freaks out (or is more likely to freak out than it is likely to freak out over custom attributes). We wanted drag-and-drop, so putting it purely in the DOM was out, as was jquery's .data() (the extra data didn't get copied properly). We probably also needed the extra data to come along for the ride in .html(). In the end we settled on using <td width="1234" rs-width="#INSWIDTH_42#"> during the editing process, and then when we POST it all, we remove width and do a regex search-and-destroy s/rs-width=/width=/g.
At first the guy writing most of this was the validation-nazi on this issue and tried everything to avoid our custom attribute, but in the end acquiesced when nothing else seemed to work for ALL our requirements. It helped when he realized that the custom attribute would never appear in an email We did consider encoding our extra data in class, but decided that would be the greater of two evils.
Personally, I prefer to have things clean and passing validators etc., but as a company employee I have to remember that my primary responsibility is advancing the company's cause (making as much money as quickly as possible), not that of my egotistical desire for technical purity. Tools should work for us; not us for them.
I know people are against it, but I came up with a super short solution for this. If you want to use a custom attribute like "mine" so for example:
Test
Then you can run this code to get an object back just like jquery.data() does.
var custom_props = {} ;
$.each($(".selector")[0].attributes, function(i,x) {
if (this.specified && x.name.indexOf("mine-") !== -1)
self.new_settings[x.name.replace("modal-","")] = x.value;
});
For complex web apps, I drop custom attributes all over the place.
For more public facing pages I use the "rel" attribute and dump all my data there in JSON and then decode it with MooTools or jQuery:
<a rel="{color:red, awesome:true, food: tacos}">blah</a>
I'm trying to stick with HTML 5 data attribute lately just to "prepare", but it hasn't come naturally yet.
Spec: Create an ASP.NET TextBox control which dynamically auto-formats its text as a number, according to properties "DecimalSeparator" and "ThousandsSeparator", using JavaScript.
One way to transfer these properties from the control to JavaScript is to have the control render out custom properties:
<input type="text" id="" decimalseparator="." thousandsseparator="," />
Custom properties are easily accessible by JavaScript. And whilst a page using elements with custom properties won't validate, the rendering of that page won't be affected.
I only use this approach when I want to associate simple types like strings and integers to HTML elements for use with JavaScript. If I want to make HTML elements easier to identify, I'll make use of the class and id properties.
I use custom fields all the time for example <a i="" .... Then reference to i with jquery. Invalid html , yes. It works well, yes.
Contrary to answers which say custom attributes won't validate:
Custom attributes will validate.
So will custom tags, as long as the custom tags are lowercase and hyphenated.
Try this in any validator. It will validate.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Custom Test</title>
</head>
<body>
<dog-cat PIANO="yellow">test</dog-cat>
</body>
</html>
Some validators:
https://appdevtools.com/html-validator
https://www.freeformatter.com/html-validator.html
https://validator.w3.org/nu/
The question is: Is it safe? Will it break later?
Custom Tags
No hyphenated tags exist. I believe that W3C will never use a hyphenated tag. And if they did, as long as you use an uncommon prefix, you'll never see a conflict. Eg.<johny-mytag>.
Custom Attributes
There are hyphenated HTML attributes. But the HTML spec promises never to use an attribute starting with data-. So data-myattrib is guaranteed to be safe. However, i believe that W3C will never introduce any attribute that starts with johny-. As long as your prefix is unusual, you'll never see a conflict.
Custom attributes, in my humble opinion, should not be used as they do not validate. Alternative to that, you can define many classes for a single element like:
<div class='class1 class2 class3'>
Lorem ipsum
</div>