Warning: not duplicate with existing questions, read through
I know I can have an event listen on changes on an contenteditable element.
What I would like is to be able to know what the changes are.
For example:
inserted "This is a sentence." at position X.
deleted from position X to Y.
formatted from X to Y with <strong>
Is that possible? (other than by doing a diff I mean)
The reason for this is to make a WYSIWYG editor of other languages than HTML, for example Markdown.
So I'd like to apply the changes to the Markdown source (instead of having to go from HTML to Markdown).
You may be able to do something with MutationObservers (falling back to DOM Mutation events in older browsers, although IE <= 8 supports neither) but I suspect it will still be hard work to achieve what you want.
Here's a simple example using MutationObservers:
http://jsfiddle.net/timdown/4n2Gz/
Sorry, but there is no way to find out what the changes are without doing a diff between the original content and the modified one when changes occur.
Are you looking for this
var strong=document.createElement("strong");
var range=window.getSelection().toString().getRangeAt(0);
range.surroundContents(strong);
this was for third part
You just need to select what you want to surround using real User interaction.
If you wanna do it dynamically
var range=document.createRange();
range.setStart(parentNode[textNode],index to start[X])
range.setEnd(parentNode[textNode],index to end[Y])
range.surroundContents(strong);
For 2nd Part
range.deleteContents()
1st part can be done by using simple iteration
var textnode=// node of the Element you are working with
textnode.splitText(offset)
offset- position about which text node splitting takes place[here==X]
Two child Nodes have been created of the parent editable Element
Now use simple insertBefore() on parent editable Element Node.
hope you will find it useful
The API you're looking for does not exist, as DOM nodes do not store their previous states.
The data / events you're wishing to get back are not native implementations in any browser Ive come across, and I struggle to think of a datatype that would be able to generically handle all those cases. perhaps something like this:
function getChanges() {
/* do stuff here to analyse changes */
var change = {
changeType : 'contentAdded',
changeStart : 50, /* beginning character */
changeContent : 'This is a sentence'
}
return change;
}
Since you're trying to get custom events / data, you're probably going to need a custom module or micro-library. Either way, to look at the changes of something, you need somehow be aware of what has changed, which can only be done by comparing what it was to what it is now.
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!
I'm making a page which has some interaction provided by javascript. Just as an example: links which send an AJAX request to get the content of articles and then display that data in a div. Obviously in this example, I need each link to store an extra bit of information: the id of the article. The way I've been handling it in case was to put that information in the href link this:
<a class="article" href="#5">
I then use jQuery to find the a.article elements and attach the appropriate event handler. (don't get too hung up on the usability or semantics here, it's just an example)
Anyway, this method works, but it smells a bit, and isn't extensible at all (what happens if the click function has more than one parameter? what if some of those parameters are optional?)
The immediately obvious answer was to use attributes on the element. I mean, that's what they're for, right? (Kind of).
<a articleid="5" href="link/for/non-js-users.html">
In my recent question I asked if this method was valid, and it turns out that short of defining my own DTD (I don't), then no, it's not valid or reliable. A common response was to put the data into the class attribute (though that might have been because of my poorly-chosen example), but to me, this smells even more. Yes it's technically valid, but it's not a great solution.
Another method I'd used in the past was to actually generate some JS and insert it into the page in a <script> tag, creating a struct which would associate with the object.
var myData = {
link0 : {
articleId : 5,
target : '#showMessage'
// etc...
},
link1 : {
articleId : 13
}
};
<a href="..." id="link0">
But this can be a real pain in butt to maintain and is generally just very messy.
So, to get to the question, how do you store arbitrary pieces of information for HTML tags?
Which version of HTML are you using?
In HTML 5, it is totally valid to have custom attributes prefixed with data-, e.g.
<div data-internalid="1337"></div>
In XHTML, this is not really valid. If you are in XHTML 1.1 mode, the browser will probably complain about it, but in 1.0 mode, most browsers will just silently ignore it.
If I were you, I would follow the script based approach. You could make it automatically generated on server side so that it's not a pain in the back to maintain.
If you are using jQuery already then you should leverage the "data" method which is the recommended method for storing arbitrary data on a dom element with jQuery.
To store something:
$('#myElId').data('nameYourData', { foo: 'bar' });
To retrieve data:
var myData = $('#myElId').data('nameYourData');
That is all that there is to it but take a look at the jQuery documentation for more info/examples.
Just another way, I personally wouldn't use this but it works (assure your JSON is valid because eval() is dangerous).
<a class="article" href="link/for/non-js-users.html">
<span style="display: none;">{"id": 1, "title":"Something"}</span>
Text of Link
</a>
// javascript
var article = document.getElementsByClassName("article")[0];
var data = eval(article.childNodes[0].innerHTML);
Arbitrary attributes are not valid, but are perfectly reliable in modern browsers. If you are setting the properties via javascript, than you don't have to worry about validation as well.
An alternative is to set attributes in javascript. jQuery has a nice utility method just for that purpose, or you can roll your own.
A hack that's going to work with pretty much every possible browser is to use open classes like this: <a class='data\_articleid\_5' href="link/for/non-js-users.html>;
This is not all that elegant to the purists, but it's universally supported, standard-compliant, and very easy to manipulate. It really seems like the best possible method. If you serialize, modify, copy your tags, or do pretty much anything else, data will stay attached, copied etc.
The only problem is that you cannot store non-serializable objects that way, and there might be limits if you put something really huge there.
A second way is to use fake attributes like: <a articleid='5' href="link/for/non-js-users.html">
This is more elegant, but breaks standard, and I'm not 100% sure about support. Many browsers support it fully, I think IE6 supports JS access for it but not CSS selectors (which doesn't really matter here), maybe some browsers will be completely confused, you need to check it.
Doing funny things like serializing and deserializing would be even more dangerous.
Using ids to pure JS hash mostly works, except when you try to copy your tags. If you have tag <a href="..." id="link0">, copy it via standard JS methods, and then try to modify data attached to just one copy, the other copy will be modified.
It's not a problem if you don't copy tags, or use read only data. If you copy tags and they're modified you'll need to handle that manually.
Using jquery,
to store: $('#element_id').data('extra_tag', 'extra_info');
to retrieve: $('#element_id').data('extra_tag');
I know that you're currently using jQuery, but what if you defined the onclick handler inline. Then you could do:
<a href='/link/for/non-js-users.htm' onclick='loadContent(5);return false;'>
Article 5</a>
You could use hidden input tags. I get no validation errors at w3.org with this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang='en' xml:lang='en' xmlns='http://www.w3.org/1999/xhtml'>
<head>
<meta content="text/html;charset=UTF-8" http-equiv="content-type" />
<title>Hello</title>
</head>
<body>
<div>
<a class="article" href="link/for/non-js-users.html">
<input style="display: none" name="articleid" type="hidden" value="5" />
</a>
</div>
</body>
</html>
With jQuery you'd get the article ID with something like (not tested):
$('.article input[name=articleid]').val();
But I'd recommend HTML5 if that is an option.
Why not make use of the meaningful data already there, instead of adding arbitrary data?
i.e. use <a href="/articles/5/page-title" class="article-link">, and then you can programmatically get all article links on the page (via the classname) and the article ID (matching the regex /articles\/(\d+)/ against this.href).
As a jQuery user I would use the Metadata plugin. The HTML looks clean, it validates, and you can embed anything that can be described using JSON notation.
This is good advice. Thanks to #Prestaul
If you are using jQuery already then you should leverage the "data"
method which is the recommended method for storing arbitrary data on a
dom element with jQuery.
Very true, but what if you want to store arbitrary data in plain-old HTML? Here's yet another alternative...
<input type="hidden" name="whatever" value="foobar"/>
Put your data in the name and value attributes of a hidden input element. This might be useful if the server is generating HTML (i.e. a PHP script or whatever), and your JavaScript code is going to use this information later.
Admittedly, not the cleanest, but it's an alternative. It's compatible with all
browsers and is valid XHTML. You should NOT use custom attributes, nor should you really use attributes with the 'data-' prefix, as it might not work on all browsers. And, in addition, your document will not pass W3C validation.
As long as you're actual work is done serverside, why would you need custom information in the html tags in the output anyway? all you need to know back on the server is an index into whatever kind of list of structures with your custom info. I think you're looking to store the information in the wrong place.
I will recognize, however unfortunate, that in lots of cases the right solution isn't the right solution. In which case I would strongly suggest generating some javascript to hold the extra information.
Many years later:
This question was posted roughly three years before data-... attributes became a valid option with the advent of html 5 so the truth has shifted and the original answer I gave is no longer relevant. Now I'd suggest to use data attributes instead.
<a data-articleId="5" href="link/for/non-js-users.html">
<script>
let anchors = document.getElementsByTagName('a');
for (let anchor of anchors) {
let articleId = anchor.dataset.articleId;
}
</script>
I advocate use of the "rel" attribute. The XHTML validates, the attribute itself is rarely used, and the data is efficiently retrieved.
So there should be four choices to do so:
Put the data in the id attribute.
Put the data in the arbitrary attribute
Put the data in class attribute
Put your data in another tag
http://www.shanison.com/?p=321
You could use the data- prefix of your own made attribute of a random element (<span data-randomname="Data goes here..."></span>), but this is only valid in HTML5. Thus browsers may complain about validity.
You could also use a <span style="display: none;">Data goes here...</span> tag. But this way you can not use the attribute functions, and if css and js is turned off, this is not really a neat solution either.
But what I personally prefer is the following:
<input type="hidden" title="Your key..." value="Your value..." />
The input will in all cases be hidden, the attributes are completely valid, and it will not get sent if it is within a <form> tag, since it has not got any name, right?
Above all, the attributes are really easy to remember and the code looks nice and easy to understand. You could even put an ID-attribute in it, so you can easily access it with JavaScript as well, and access the key-value pair with input.title; input.value.
One possibility might be:
Create a new div to hold all the extended/arbitrary data
Do something to ensure that this div is invisible (e.g. CSS plus a class attribute of the div)
Put the extended/arbitrary data within [X]HTML tags (e.g. as text within cells of a table, or anything else you might like) within this invisible div
Another approach can be to store a key:value pair as a simple class using the following syntax :
<div id="my_div" class="foo:'bar'">...</div>
This is valid and can easily be retrieved with jQuery selectors or a custom made function.
In html, we can store custom attributes with the prefix 'data-' before the attribute name like
<p data-animal='dog'>This animal is a dog.</p>.
Check documentation
We can use this property to dynamically set and get attributes using jQuery like:
If we have a p tag like
<p id='animal'>This animal is a dog.</p>
Then to create an attribute called 'breed' for the above tag, we can write:
$('#animal').attr('data-breed', 'pug');
To retrieve the data anytime, we can write:
var breedtype = $('#animal').data('breed');
At my previous employer, we used custom HTML tags all the time to hold info about the form elements. The catch: We knew that the user was forced to use IE.
It didn't work well for FireFox at the time. I don't know if FireFox has changed this or not, but be aware that adding your own attributes to HTML elements may or may-not be supported by your reader's browser.
If you can control which browser your reader is using (i.e. an internal web applet for a corporation), then by all means, try it. What can it hurt, right?
This is how I do you ajax pages... its a pretty easy method...
function ajax_urls() {
var objApps= ['ads','user'];
$("a.ajx").each(function(){
var url = $(this).attr('href');
for ( var i=0;i< objApps.length;i++ ) {
if (url.indexOf("/"+objApps[i]+"/")>-1) {
$(this).attr("href",url.replace("/"+objApps[i]+"/","/"+objApps[i]+"/#p="));
}
}
});
}
How this works is it basically looks at all URLs that have the class 'ajx' and it replaces a keyword and adds the # sign... so if js is turned off then the urls would act as they normally do... all "apps" (each section of the site) has its own keyword... so all i need to do is add to the js array above to add more pages...
So for example my current settings are set to:
var objApps= ['ads','user'];
So if i have a url such as:
www.domain.com/ads/3923/bla/dada/bla
the js script would replace the /ads/ part so my URL would end up being
www.domain.com/ads/#p=3923/bla/dada/bla
Then I use jquery bbq plugin to load the page accordingly...
http://benalman.com/projects/jquery-bbq-plugin/
I have found the metadata plugin to be an excellent solution to the problem of storing arbitrary data with the html tag in a way that makes it easy to retrieve and use with jQuery.
Important: The actual file you include is is only 5 kb and not 37 kb (which is the size of the complete download package)
Here is an example of it being used to store values I use when generating a google analytics tracking event (note: data.label and data.value happen to be optional params)
$(function () {
$.each($(".ga-event"), function (index, value) {
$(value).click(function () {
var data = $(value).metadata();
if (data.label && data.value) {
_gaq.push(['_trackEvent', data.category, data.action, data.label, data.value]);
} else if (data.label) {
_gaq.push(['_trackEvent', data.category, data.action, data.label]);
} else {
_gaq.push(['_trackEvent', data.category, data.action]);
}
});
});
});
<input class="ga-event {category:'button', action:'click', label:'test', value:99}" type="button" value="Test"/>
My answer might not apply to your case. I needed to store a 2D table in HTML, and i needed to do with fewest possible keystrokes. Here's my data in HTML:
<span hidden id="my-data">
IMG,,LINK,,CAPTION
mypic.jpg,,khangssite.com,,Khang Le
funnypic.jpg,,samssite.com,,Smith, Sam
sadpic.png,,joyssite.com,,Joy Jones
sue.jpg,,suessite.com,,Sue Sneed
dog.jpg,,dogssite.com,,Brown Dog
cat.jpg,,catssite.com,,Black Cat
</span>
Explanation
It's hidden using hidden attribute. No CSS needed.
This is processed by Javascript. I use two split statements, first on newline, then on double-comma delimiter. That puts the whole thing into a 2D array.
I wanted to minimize typing. I didn't want to redundantly retype the fieldnames on every row (json/jso style), so i just put the fieldnames on the first row. That a visual key for the programmer, and also used by Javascript to know the fieldnames. I eliminated all braces, brackets, equals, parens, etc. End-of-line is record delimiter.
I use double-commas as delimiters. I figured no one would normally use double-commas for anything, and they're easy to type. Beware, programmer must enter a space for any empty cells, to prevent unintended double-commas. The programmer can easily use a different delimiter if they prefer, as long as they update the Javascript. You can use single-commas if you're sure there will be no embedded commas within a cell.
It's a span to ensure it takes up no room on the page.
Here's the Javascript:
// pull 2D text-data into array
let sRawData = document.querySelector("#my-data").innerHTML.trim();
// get headers from first row of data and load to array. Trim and split.
const headersEnd = sRawData.indexOf("\n");
const headers = sRawData.slice(0, headersEnd).trim().split(",,");
// load remaining rows to array. Trim and split.
const aRows = sRawData.slice(headersEnd).trim().split("\n");
// trim and split columns
const data = aRows.map((element) => {
return element.trim().split(",,");
});
Explanation:
JS uses lots of trims to get rid of any extra whitespace.