Related
When I am using jQuery to grab CSS values for objects, each of the browsers (IE, Mozilla, Chrome, etc) returns different values.
For example, in Chrome, a background image (.css("background-image")) returns:
url(http://i41.tinypic.com/f01zsy.jpg)
Where in Mozilla, it returns:
url("http://i41.tinypic.com/f01zsy.jpg")
I am having the same problem on other aspects, such as background-size.
In chrome it returns:
50% 50%
But Mozilla returns:
50%+50%
My problem with this is, I have functions that split the CSS (background-size), for example based on a space .split(" "), but this could not work on Mozilla because it uses a + instead.
Is there any way that I can fix this problem and make the browsers to use one standard?
Is there any function that I could write which grabs and splits values, based on the type of browser the user is using?
My problem with this is, I have functions that split the CSS
(background-size), for example based on a space .split(" "), but this
could not work on Mozilla because it uses a + instead.
Try adding \+ to RegExp passed to .split
.split(/\s|\+/)
var res = ["50%+50%", "50% 50%"];
var re = /\s+|\+/;
console.log(res[0].split(re), res[1].split(re));
Different browsers use different CSS standards and you may have to write a full-blown parser to make them one standard.
Workaround is that you should split or use CSS values taking into account the different browsers standards. Like the CSS(background-size) problem can be solved using this:
space.split("\\s|\\+"); //split my string where it either has a space 'or' a plus sign
For CSS(background-image), the solution may be to replace the inverted commas before using it:
space.replace("\"", "");
Try to make the splits generallized for all browsers. Hope that helps.
This probably isn't the cleanest method, but you could run a string parser for the background image source and delete any quotation marks. This would be the most efficient method for parsing the background image URL. It should work without harming the data because URL's typically can't contain quotation marks, as they are encoded as %22
As for the background-size, you could parse the results for + signs and change those to spaces, as + signs typically aren't present as the values for any CSS properties, so you should be relatively safe in taking those out.
In addition, you could check the browser type to see if you'd even have to run these parsings in the first place. As a precaution, you should also see how Opera and Safari return results, and if those are any different, you could create branch statements for the parsers that handle the different types of CSS values returned by the different browsers.
Note: The parsing methods I have described attempt the goal of converting the Firefox results to the Chrome-style results.
Thanks for all the help.
I'll share the code I have ended up using!
cssCommas: function(text)
{
return text.replace(new RegExp("\"", "g"),"");
},
cssPlus: function(text)
{
return text.replace(new RegExp("\\+", "g"),"");
},
cssSplit: function(text,removePercent)
{
var removeParent = removeParent || false;
if(removePercent == true)
{
text = text.replace(new RegExp("%", "g"),"");
}
return text.split(new RegExp("\\s|\\+","g"));
},
css: function(text)
{
return this.cssCommas(this.cssPlus(text));
}
Works perfectly on all browsers now. Thanks a lot.
When I create web page, I want all features on the page to be placed on the same position in different browsers. Sometimes some features are placed on different positions, when I run the page on different browsers.I am using these variables to determine what browser is being opened right know and with if statement for example I determine some options.
//var firefox = (navigator.userAgent.indexOf("Firefox") != -1);
//var opera = (navigator.userAgent.indexOf("Opera") != -1);
//var ie = (navigator.userAgent.indexOf("MSIE") != -1);
//var safari = (navigator.userAgent.indexOf("Safari") != -1);
//var scrome = (navigator.userAgent.indexOf("Chrome") != -1);
1. Is it a good way to do like this when some features are placed on different positions in different browsers ?
2.What type is returned when I am using 'navigator.userAgent.indexOf(...)'. Is it boolean or some kind of collection which returns the position of the parameter, like the hashtable does ?. What type returns exactly ?
Different browsers may have different behavior of initial positions, a good practice to make them behaves same is to do CSS reset.
navigator.userAgent is a string object, thus .indexOf(...) returns integer Number
(1) don't do browser detection based on the user agent string. This is highly unreliable and very much not 2013. You want to use a framework like modernizr to detect which features are supported (and which are not). This is a good start.
(2) Not a collection, just a number that describes the position of the searched string in the original string. See here, or find out yourself:
typeof(navigator.userAgent.indexOf("foo"))
(3) BTW: amazing name for posting beginner questions ;)
so you r looking for this i think :
<!--[if IE]>
<link rel="stylesheet" type="text/css" href="style.css" />
<!-->
that code should be place in the Head section of the page you could specify a multiple css page for each browser this link is for IE(internet explorer) for example
I'm running document.querySelectorAll() frequently, and would like a short alias for it.
var queryAll = document.querySelectorAll
queryAll('body')
TypeError: Illegal invocation
Doesn't work. Whereas:
document.querySelectorAll('body')
Still does. How can I make the alias work?
This seems to work:
const queryAll = document.querySelectorAll.bind(document);
bind returns a new function which works identically to the querySelectorAll function, where the value of this inside the querySelectorAll method is bound to the document object.
The bind function is only supported in IE9+ (and all the other browsers) - https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind
Update: In fact you could create shortcuts to a whole range of document methods like this:
const query = document.querySelector.bind(document);
const queryAll = document.querySelectorAll.bind(document);
const fromId = document.getElementById.bind(document);
const fromClass = document.getElementsByClassName.bind(document);
const fromTag = document.getElementsByTagName.bind(document);
A common answer is to use $ and $$ for querySelector and querySelectorAll. This alias mimics jQuery's one.
Example:
const $ = document.querySelector.bind(document)
const $$ = document.querySelectorAll.bind(document)
$('div').style.color = 'blue'
$$('div').forEach(div => div.style.background = 'orange')
div {
margin: 2px;
}
<div>
test
</div>
<section>
<div>
hello
</div>
<div>
foo
</div>
</section>
The JavaScript interpreter throws an error because querySelectorAll() should be invoked in document context.
The same error is thrown when you are trying to call console.log() aliased.
So you need to wrap it like this:
function x(selector) {
return document.querySelectorAll(selector);
}
My solution covers the four following use cases:
document.querySelector(...)
document.querySelectorAll(...)
element.querySelector(...)
element.querySelectorAll(...)
The code:
let doc=document,
qsa=(s,o=doc)=>o.querySelectorAll(s),
qs=(s,o=doc)=>o.querySelector(s);
In terms of parameters, the selector s is required, but the container element object o is optional.
Usage:
qs("div"): Queries the whole document for the first div, returns that element
qsa("div"): Queries the whole document for all divs, returns a nodeList of all those elements
qs("div", myContainer): Queries just within the myContainer element for the first div, returns that element
qsa("div", myContainer): Queries just within the myContainer element for all divs, returns a nodeList of all those elements
To make the code slightly shorter (but not quite as efficient), the qs code could be written as follows:
let qs=(s,o=doc)=>qsa(s,o)[0];
The code above uses ES6 features (let, arrow functions and default parameter values). An ES5 equivalent is:
var doc=document,
qsa=function(s,o){return(o||doc).querySelectorAll(s);},
qs=function(s,o){return(o||doc).querySelector(s);};
or the equivalent shorter but less efficient ES5 version of qs:
var qs=function(s,o){return qsa(s,o)[0];};
Below is a working demo. To ensure it works on all browsers, it uses the ES5 version, but if you're going to use this idea, remember that the ES6 version is shorter:
var doc = document;
var qs=function(s,o){return(o||doc).querySelector(s);},
qsa=function(s,o){return(o||doc).querySelectorAll(s);}
var show=function(s){doc.body.appendChild(doc.createElement("p")).innerHTML=s;}
// ____demo____ _____long equivalent______ __check return___ _expect__
// | | | | | | | |
let one = qs("div"); /* doc.querySelector ("#one") */ show(one .id ); // "one"
let oneN = qs("div",one); /* one.querySelector ("div") */ show(oneN .id ); // "oneNested"
let many = qsa("div"); /* doc.querySelectorAll("div") */ show(many .length); // 3
let manyN = qsa("div",one); /* one.querySelectorAll("div") */ show(manyN.length); // 2
<h3>Expecting "one", "oneNested", 3 and 2...</h3>
<div id="one">
<div id="oneNested"></div>
<div></div>
</div>
This would work, you need to invoke the alias using call() or apply() with the appropriate context.
func.call(context, arg1, arg2, ...)
func.apply(context, [args])
var x = document.querySelectorAll;
x.call(document, 'body');
x.apply(document, ['body']);
I took #David Muller's approach and one-lined it using a lambda
let $ = (selector) => document.querySelector(selector);
let $all = (selector) => document.querySelectorAll(selector);
Example:
$('body');
// <body>...</body>
function x(expr)
{
return document.querySelectorAll(expr);
}
If you don't care about supporting ancient, awful browsers that nobody should be using anymore, then you can just do this:
const $ = (sel, parent = document) => parent.querySelector(sel);
const $$ = (sel, parent = document) => Array.from(parent.querySelectorAll(sel));
Here's some examples of usage:
// find specific element by id
console.log($("#someid"));
// find every element by class, within other element
// NOTE: This is a contrived example to demonstrate the parent search feature.
// If you don't already have the parent in a JavaScript variable, you should
// query via $$("#someparent .someclass") for better performance instead.
console.log($$(".someclass", $("#someparent")));
// find every h1 element
console.log($$("h1"));
// find every h1 element, within other element
console.log($$("h1", $("#exampleparent")));
// alternative way of finding every h1 element within other element
console.log($$("#exampleparent h1"));
// example of finding an element and then checking if it contains another element
console.log($("#exampleparent").contains($("#otherelement")));
// example of finding a bunch of elements and then further filtering them by criteria
// that aren't supported by pure CSS, such as their text content
// NOTE: There WAS a ":contains(text)" selector in CSS3 but it was deprecated from the
// spec because it violated the separation between stylesheets and text content, and you
// can't rely on that CSS selector, which is why you should never use it and should
// instead re-implement it yourself like we do in this example.
// ALSO NOTE: This is just a demonstration of .filter(). I don't recommend using
// "textContent" as a filter. If you need to find specific elements, use their way
// more reliable id/class to find them instead of some arbitrary text content search.
console.log($$("#exampleparent h1").filter(el => el.textContent === "Hello World!"));
The functions I provided use a ton of modern JS features:
const to make sure the function variable can't be overwritten.
functions defined as an arrow function aka lambda: (args) => code with implied return statement
default parameters (not supported by browsers before the year 2016)
no {} or return, since those can be skipped if there's just 1 statement in the function body.
The modern function Array.from() is used, which converts the querySelectorAll result (which is always a NodeList, or empty NodeList), into an Array, which is basically what every developer wants. Because that's how you get access to .filter() and other Array functions that allow you to process the discovered nodes further, using clean, short code. And Array.from() creates a shallow copy of all elements which means that it's blazingly fast (it just copies the memory references/pointers to each Node DOM element from the original NodeList). It's a major API enhancer.
If you care about ancient browsers, you can still use my functions but use Babel to convert the modern JS to old ES5 when you release your website.
My suggestion would be to write your entire site in ES6 or higher and then use Babel if you care about visitors from Windows XP and other dead operating systems, or just random people who haven't updated their browsers in 5+ years.
But I wouldn't recommend using Babel. Stop worrying about people who have old browsers. It is their problem, not yours.
The modern "app" world is incredibly deeply based on web browsers and JavaScript and modern CSS, and most of your visitors these days have modern, auto-updated browsers. You basically can't live modern life without a modern browser, since so many websites demand it now.
In my opinion, the days of expecting web designers to waste their time and sanity trying to make a site work on browsers from 1993 are over. It's time the laziest customers/visitors update their old browsers instead. It's not difficult. Even old and dead operating systems usually have ways to install new versions of browsers on them. And people who don't have an updated browser are only a tiny fraction of a percent these days.
For example, the Bootstrap framework, the world's most popular framework for mobile/responsive sites, only cares about supporting the 2 most recent major versions of all major browsers (at least 0.5% market share). Here's their list as of this moment:
0.5% market share or higher
last 2 major versions only
not a dead browser (not discontinued)
Chrome >= 60
Firefox >= 60
Firefox ESR
iOS >= 12
Safari >= 12
not Explorer <= 11 (meaning no versions of Internet Explorer at all)
And I completely agree with this. I was a web developer in the early 2000s and it was absolute hell. Being expected to make it work on some random, stupid user's ancient browser was hell. It took all the fun out of web development. It made me hate and quit web development. Because 90% of my time was wasted on browser compatibility. That's not how life should be. It's not your fault that some customers/visitors are lazy. And these days, visitors have no more excuses to stay lazy.
Instead, you should only target users who have modern browsers. Which is basically everybody these days. There is no excuse for anyone to use an old browser. And if they use an old browser, your site should show a big, fat banner saying "Please, join the modern world for your own sake. Download a new browser. How are you even able to live your normal life with such an old browser? Are you a time traveler from caveman times?".
People have no excuses to have old browsers anymore:
Linux: Ships with the latest versions of Firefox by default.
Mac: Ships with Safari, which is a modern browser. But some older macOS versions won't get newer versions of Safari, and older machines often can't install the latest macOS version either. Well, tough luck for those visitors. They are gonna have trouble on more than just your website. It's up to them to install a modern browser (such as Chrome), which they are able to do even on old versions of macOS. So they have no excuses. Don't waste your life catering to people on very old, buggy Safari versions.
Windows: Windows 10 ships with Edge, which is based on Chromium and is as compatible with websites as Chrome is. People have no excuse to have an old browser. And most Windows users use Chrome. As for very old, discontinued versions of Windows (XP, Vista, 7, 8), well, we yet again arrive at the same question as before: Do you care about 0.0000001% stupid visitors who use a dead OS and an old Internet Explorer version? The whole freaking web will be broken for them anyway, so who cares if your site is broken for them too? They should stop being lazy and just upgrade their OS to Windows 10, or at least install Chrome or Firefox on their current OS. They have no excuses.
iOS: If you're stuck on a super old iOS device, then you can't use the modern web. Tough luck. A lot of the web is gonna be broken for you. Get a new device. Even frameworks like Bootstrap, the world's #1 mobile web framework, doesn't support iOS 11 or earlier. It's not our problem. It's the cheapskate visitor's problem if they still hang onto such an old device. They can literally get a newer iOS device second-hand for almost no money at all and fix all of their problems with visiting the modern web. And they'll need to buy that anyway since most apps (even banking/important apps) require modern iOS versions.
Android: The browser is independently updated from the OS, and can even be sideloaded, so even if you're stuck on old Android versions, you have access to modern browsers. So you have no excuses.
Most people these days have browsers that are completely up-to-date and auto-updated. That's the fact.
So yeah... the days of website designers suffering through hell just for catering to old browsers are over. Therefore I suggest that people use ES6 and CSS3 for their websites, to make web designing a joy for the first time.
Hope you enjoy the ES6 functions I provided!
Here is my take on it. If the selector has multiple matches, return like querySelectorAll. If ony one match is found return like querySelector.
function $(selector) {
let all = document.querySelectorAll(selector);
if(all.length == 1) return all[0];
return all;
}
let one = $('#my-single-element');
let many = $('#multiple-elements li');
2019 update
Today I made a new take on the problem. In this version you can also use a base like this:
let base = document.querySelectorAll('ul');
$$('li'); // All li
$$('li', base); // All li within ul
Functions
function $(selector, base = null) {
base = (base === null) ? document : base;
return base.querySelector(selector);
}
function $$(selector, base = null) {
base = (base === null) ? document : base;
return base.querySelectorAll(selector);
}
The highly invasive version:
<script>
for(const c of [HTMLDocument, Element, DocumentFragment]) {
c.prototype.$ = c.prototype.querySelector;
c.prototype.$$ = c.prototype.querySelectorAll;
}
const $ = document.$.bind(document); // For inline events
const $$ = document.$$.bind(document);
window.$ = $; // For JS files
window.$$ = $$;
</script>
So you can chain $('nav').$$('a') like jQuery allows (I think). Since ShadowRoot extends DocumentFragment you can even inspect (open) Shadow DOMs:
const styles = $('x-button').shadowRoot.$$('style');
function $(selector, base = null) {
base = (base === null) ? document : base;
return base.querySelector(selector);
}
function $$(selector, base = null) {
base = (base === null) ? document : base;
return base.querySelectorAll(selector);
}
Why not simplier ??? :
let $ = (selector, base = document) => {
let elements = base.querySelectorAll(selector);
return (elements.length == 1) ? elements[0] : elements;
}
you dont need to add any dot before class name you can modify this also by using getEle by class or id tag etc
function $(selector){
var dot = ".";
var newSelector = dot.concat(selector);
var Check = document.querySelectorAll(newSelector);
if(Check && Check.length>0 && Check.length < 2){
return document.querySelector(newSelector);
}
else {
return document.querySelectorAll(newSelector);
}
}
$("answercell")[0].remove();
The goal: To find a cross-platform solution for displaying numeric keyboards on mobile touch-based devices, with a minimum of hacks.
The problem:
I have a regular web application using data input forms, containing primarily numeric data. When a user interacts with my site on a mobile device, I would like to display a numeric virtual keypad as most standard keyboards require a second press to switch from alphas to numbers. I know that i can trigger different keyboard by setting the "type" attribute of input element:
type=number:
This works great under iOS/Safari. I am unsure about other browsers on the platform.
On Android, this does not consistently raise the correct keyboard on various browsers and often results in unwanted elevator buttons on the input. I have yet to find a clean way to turn these off in CSS.
type=tel:
This almost works on iOS/Safari, but the telephone keyboard lacks a decimal button.
Seems to work great across multiple android browsers, without any extraneous UI elements added to the page.
My current solution is hacky and simplistic. Based on a class that I'm already using for numeric validation, I replace each text element that should contain a number with a new input that is either a number or a tel type based on the detected OS/browser.
var isAndroid = navigator.userAgent.match(/android/i) ? true : false;
var isIOS = navigator.userAgent.match(/(ipod|ipad|iphone)/i) ? true : false;
if (isAndroid || isIOS) {
var useNumberType = (isIOS ? true : false); //iOS uses type=number, everyone else uses type=tel
jQuery("input.num").each(function () {
var type = (useNumberType ? "number" : "tel");
var html = this.outerHTML;
html = html.replace(/(type=\"?)text(\"?)/, "$1" + type + "$2");
this.outerHTML = html;
});
}
I would prefer to not use browser detection and to not change out the inputs on the fly at run time. I could possibly introduce an http module on the server side that did basically the same thing, but that is not substantially better. I'm shocked that there isn't a CSS call for this.
Is there a better way to get a numeric keyboard with a decimal button, that works on all or most touch-based mobile devices without adding weird UI elements to the page?
-------------- update
I don't think there is a way to do what I really want to do, which is to setup a single input style or type that will work well across desktop browsers and all major mobile touch-based platforms. I settled on changing the type of the input through a direct DOM call rather through jQuery instead of rewriting the entire input via outerHTML. I suspect there isn't much difference in effect, but the code is a little cleaner. Since I'm not changing input types on the desktop, I shouldn't have to worry about IE's read only restriction on the attribute.
Ideally, I'd probably handle this on the server side so everything got sent to the browser in the format desired for the device making the request. But for now the new code looks more like this:
var isAndroid = navigator.userAgent.match(/android/i) || navigator.platform.match(/android/i) ? true : false;
var isIOS = navigator.userAgent.match(/(ipod|ipad|iphone)/i) ? true : false;
if (isAndroid || isIOS) {
var useNumberType = (isIOS ? true : false); //iOS uses type=number, everyone else uses type=tel
jQuery("input.num").each(function () {
var type = (useNumberType ? "number" : "tel");
if (this.type == "text") {
this.type = type;
}
});
}
Protip: when working with mobile, do NOT interfere with the user experience. This means keeping the built-in keypads as they are.
Some users may even have Javascript disabled on their mobile devices/browsers!
What you should do here is include an HTML hint to the browser. This way, mobile browsers should know what kind of content they are interacting with.
HTML5 includes several new <input> content types that should be supported on all modern mobile devices (and most modern browsers)
You can find the full list here.
What you want specifically is the following:
Old code:
Phone number: <input type="text" name="phone" />
New code:
Phone number: <input type="tel" name="phone" />
I don't know that any browsers currently support "tel", so you could use something like the following:
Phone number: <input type="number" name="phone" min="1000000000" max="9999999999" />
This is a bit of a hack, but is another option for you.
This is a MUCH simpler and more maintainable way of doing things, and better for the user.
Please let me know if you have any questions. I know this isn't directly answering the question, but it is a better way of doing things for now, in my opinion. :)
EDIT:
A possible way to get around this for each browser is by checking the user agent using JS/Jquery. I'm not sure exactly how to do this, but here is a tutorial on how to do so in .NET and changing the CSS for each element using JQuery.
EDIT EDIT!:
Try just modifying your code as such:
var isAndroid = navigator.userAgent.match(/android/i) ? true : false;
var isIOS = navigator.userAgent.match(/(ipod|ipad|iphone)/i) ? true : false;
if(isIOS)
$(.phoneInput).attr("type", "tel");
if(isAndroid)
{
$(.phoneInput).attr("type", "number");
$(.phoneInput).attr("min", "1000000000");
$(.phoneInput).attr("max", "9999999999");
}
I hope this out of everything works! You might have to switch the two if statements, depending on how your testing turns out.
There are several good Javascript editors for Markdown / Textile (e.g.: http://attacklab.net/showdown/, the one I'm using right now), but all I need is a Javascript function that converts a string from Markdown / Textile -> HTML and back.
What's the best way to do this? (Ideally it would be jQuery-friendly -- e.g., $("#editor").markdown_to_html())
Edit: Another way to put it is that I'm looking for a Javascript implementation of Rails' textilize() and markdown() text helpers
For Markdown -> HTML, there is Showdown
StackOverflow itself uses Markdown language for questions and answers ; did you try to take a look at how it works ?
Well, it seems it is using PageDown which is available under the MIT License
The question Is there any good Markdown Javascript library or control? and its answers might help, too :-)
A full editor is, of course, not exactly what you asked for ; but they must use some kind of function to transform the Markdown code to HTML ; and, depending on the license of these editors, you might be able to re-use that function...
Actually, if you take a close look at Showdown, in its code source (file showdown.js), you'll find this portion of comment :
//
// Showdown usage:
//
// var text = "Markdown *rocks*.";
//
// var converter = new Showdown.converter();
// var html = converter.makeHtml(text);
//
// alert(html);
//
// Note: move the sample code to the bottom of this
// file before uncommenting it.
//
It's not jQuery syntax, but should be quite easy to integrate in your application ;-)
About Textile, it seems to be a bit harder to find anything useful :-(
In the other side, HTML -> Markdown, I guess things might be a bit harder...
What I would do is store both Markdown and HTML in my application data store (database ? ), and use one for editing, and the other for rendering... Would take more space, but it seems less risky than "decrypting" HTML...
I thought it would be worthwhile to make a list here of the JavaScript solutions out there and their minified (uncompressed) size and strengths/weaknesses. Compressed size for minified code will be around 50% of uncompressed size. What it comes down to:
Use markdown-it (104KB) if you need comprehensive support and will have user edited or arbitrary documents, but are not overly concerned with size/bandwidth.
Use my own drawdown (1.3KB) if you need reasonably high quality, and table support, but want feather weight and do not need any features other than conversion, or to have every single edge case addressed.
Use one of the others if you need unique capabilities like security or expandability.
All of these use the MIT license, most are on npm.
markdown-it: 104KB. Powers StackExchange since the CommonMark migration. Follows the CommonMark spec and is now more or less the gold standard; supports syntax extensions; produces secure output by default. Fast; as robust as showdown, but very large. Has a ton of features (e.g. synced scrolling). Is also the basis for http://dillinger.io/.
showdown: 28KB. Has comprehensive CommonMark support and was previously the gold standard; is significantly smaller than Markdown-It but slower. it is the basis for pagedown.
pagedown: 8KB. Powered StackExchange before the CommonMark migration. It is very robust but missing tables, definition lists, footnotes, etc. In addition to the 8KB converter script, it also offers editor and sanitizer scripts.
drawdown: 1.3KB. Full disclosure, I wrote it. Broader feature scope than any other lightweight converter; handles most but not all of the CommonMark spec. Not recommended for user editing but very useful for presenting information in web apps. No inline HTML.
marked: 19KB. Comprehensive; tested against unit test suite; supports custom lexer rules.
micromarkdown: 5KB. Supports a lot of features, but is missing some common ones like unordered lists using * and some common ones that aren't strictly part of the spec like fenced code blocks. Many bugs, throws exceptions on most longer documents. I consider it experimental.
nano-markdown: 1.9KB. Feature scope limited to things used by most documents; more robust than micromarkdown but not perfect; uses its own very basic unit test. Reasonably robust but breaks on many edge cases.
mmd.js: 800 bytes. The result of an effort to make the smallest possible parser that is still functional. Supports a small subset; document needs to be tailored for it.
markdown-js: 54KB (not available for download minified; would probably minify to ~20KB). Looks pretty comprehensive and includes tests, but I'm not very familiar with it.
meltdown: 41KB (not available for download minified; would probably minify to ~15KB). jQuery plugin; Markdown Extra (tables, definition lists, footnotes).
unified.js: varies, 5-100KB. A plugin-based system for converting between html, markdown, and prose. Depending on what plugins you need (spell-checking, syntax-highlighting, input sanitizing) the file size will vary. Probably used more server-side than client-side.
Textile
You can find a seemingly very fine Javascript implementation of Textile here, and another one there (maybe not so good, but has a nice convert-as-you-type example page).
Note: there is a bug in the first implementation I made a link to : horizontal bars are not rendered correctly. To fix it, you can add the following code in the file.
for(i=0;i<lines.length;i++) {
// Add code :Start
if (lines[i].match(/\s*-{4,}\s*/)){
html+="<hr/>\n";
continue;
}
// Add code :End
if (lines[i].indexOf("[") == 0) {continue;}
//...
I am using the tiny minimalistic script - mmd.js, which only supports a subset of Markdown possibilities, but this might be all that one would need anyway, so this script which is less than 1kb is amazing and won't be an overkill.
Supported features
Headers #
Blockquotes >
Ordered lists 1
Unordered lists *
Paragraphs
Links []()
Images ![]()
Inline emphasis *
Inline emphasis **
Unsupported features
References and IDs
Escaping of Markdown characters
Nesting
It’s easy to use Showdown with or without jQuery. Here’s a jQuery example:
// See http://mathiasbynens.be/notes/showdown-javascript-jquery for a plain JavaScript version as well
$(function() {
// When using more than one `textarea` on your page, change the following line to match the one you’re after
var $textarea = $('textarea'),
$preview = $('<div id="preview" />').insertAfter($textarea),
converter = new Showdown.converter();
$textarea.keyup(function() {
$preview.html(converter.makeHtml($textarea.val()));
}).trigger('keyup');
});
I found this question intriguing, so I decided to start something off (only replaces strong and italic markdown tags). Having spent an hour trying to devise a solution using regexes, I gave up and ended up with the following, which seems to work nicely. That said, it can surely be further optimized and I'm not sure as to just how real-world resilient it will be in this form:
function mdToHtml(str) {
var tempStr = str;
while(tempStr.indexOf("**") !== -1) {
var firstPos = tempStr.indexOf("**");
var nextPos = tempStr.indexOf("**",firstPos + 2);
if(nextPos !== -1) {
var innerTxt = tempStr.substring(firstPos + 2,nextPos);
var strongified = '<strong>' + innerTxt + '</strong>';
tempStr = tempStr.substring(0,firstPos) + strongified + tempStr.substring(nextPos + 2,tempStr.length);
//get rid of unclosed '**'
} else {
tempStr = tempStr.replace('**','');
}
}
while(tempStr.indexOf("*") !== -1) {
var firstPos = tempStr.indexOf("*");
var nextPos = tempStr.indexOf("*",firstPos + 1);
if(nextPos !== -1) {
var innerTxt = tempStr.substring(firstPos + 1,nextPos);
var italicized = '<i>' + innerTxt + '</i>';
tempStr = tempStr.substring(0,firstPos) + italicized + tempStr.substring(nextPos + 2,tempStr.length);
//get rid of unclosed '*'
} else {
tempStr = tempStr.replace('*','');
}
}
return tempStr;
}
Test code:
var s = "This would be *italicized* text and this would be **bold** text, This would be *italicized* text and this would be **bold** text, This would be *italicized* text and this would be **bold** text";
alert(mdToHtml(s));
Output:
This would be <i>italicized</i>text and this would be <strong>bold</strong> text, This would be <i>italicized</i>text and this would be <strong>bold</strong> text, This would be <i>italicized</i>text and this would be <strong>bold</strong> text
EDIT: New in V 0.024 - Automatic removal of unclosed markdown tags
The Showdown Attacklab-Link is down so use https://github.com/coreyti/showdown for your conversion needs :)
This doesn't address the entire request (it isn't an editor), but textile-js is a javascript rendering library: https://github.com/borgar/textile-js. A demonstration is available at http://borgar.github.io/textile-js/
Let's write a simple markdown parser function that will take in a single line of markdown and be translated into the appropriate HTML. To keep it simple, we'll support only one feature of markdown in syntax: headers.
Headers are designated by (1-6) hashes followed by a space, followed by text. The number of hashes determines the header level of the HTML output.
function markdownParser(markdown) {
const htmlText = markdown
.replace(/^# (.*$)/gim, '<h1>$1</h1>')
.replace(/^## (.*$)/gim, '<h2>$1</h2>')
.replace(/^### (.*$)/gim, '<h3>$1</h3>')
.replace(/^#### (.*$)/gim, '<h4>$1</h4>')
.replace(/^##### (.*$)/gim, '<h5>$1</h5>')
.replace(/^###### (.*$)/gim, '<h6>$1</h6>')
return htmlText.trim()
}
markdown-js is a nice javascript markdown parser, an active project with tests.
Have you looked at the Eclipse WikiText library that is part of Mylyn. It will convert from many wiki syntax to xhtml and to xdocs/DITA. It looks way cool.
http://help.eclipse.org/galileo/topic/org.eclipse.mylyn.wikitext.help.ui/help/Markup-Conversion.html
Has anyone found a solution to the HTML->textile problem? All of our current documentation is in M$ Word format and we would love to bring it into Redmine Wiki for collaborative maintenance. We have not found any tool that will make the conversion. We have found the Open Office extension that produces mediawiki formatted text but Redmine Wiki uses a subset of textile.
Anyone know of a tool that converts TO textile from mediawiki, Word, XDocs, or HTML?
For Textile:
I've recently patched together an HTML to Textile converter: https://github.com/cmroanirgo/to-textile
For the reverse Textile to HTML, I use and recommend https://github.com/borgar/textile-js, which other answers have already mentioned.
I found the answer by karim79 useful. I modified it a bit, so I want to share my version. Maybe it will help someone out there.
I modified the answer to adapt it from JavaScript to PHP (I know the question doesn't have a PHP tag)
I also changed from while() to for() because:
I didn't want to replace uninterrupted sequences of asterisks like "********"
I didn't want to remove unpaired "*"
It's still not perfect, it will for example "strongify" anything between two far apart *'s, consuming both of them. But I think this is how whatsapp behaves also.
I assume there are better ways of forcing the end of the loop than what I did, I just followed the rule "if it aint broken, don't fix it". I'm happy to receive suggestions
function md2html($text = "") {
// I constantly check for length of text, instead of assigning the value to a var (which would be more efficient) because the length changes with every replacement!
for ($i = 0; $i < strlen($text); $i++) {
if (($beg = strpos($text,"*",$i)) !== FALSE){
if (($seq = strspn($text,"*",$beg)) > 1)
$i = $beg + $seq; // skip sequence
else {
if (($end = strpos($text,"*",$beg + 1)) !== FALSE){ // found a second one
$length = $end - $beg - 1;
$innerText = substr($text,$beg+1,$length);
$strongified = "<strong>$innerText</strong>";
// following the original answer I should do something like
// $text = substr($text,0,$beg) . $strongified . substr($text,$end+1);
// but I assume the following is better:
$text = substr_replace($text,$strongified,$beg,$length+2); // adding to length to include both asterisks
$i = $end + 16; // the real position in the string has now increased due to replacement
} else
$i = strlen($text); // force end of for loop
}
} else
$i = strlen($text);
}
for ($i = 0; $i < strlen($text); $i++) {
if (($beg = strpos($text,"_",$i)) !== FALSE){
if (($seq = strspn($text,"_",$beg)) > 1)
$i = $beg + $seq;
else {
if (($end = strpos($text,"_",$beg + 1)) !== FALSE){
$length = $end - $beg - 1;
$innerText = substr($text,$beg+1,$length);
$italicized = "<em>$innerText</em>";
$text = substr_replace($text,$italicized,$beg,$length+2);
$i = $end + 10;
} else
$i = strlen($text);
}
} else
$i = strlen($text);
}
return $text;
}