A CSS selector is made up of five elements:

  1. The element type (tag name)
  2. The id of the element
  3. The classes of the element
  4. The attributes of the element
  5. Operators to chain these element selectors

This challenge will only involve the first four--also taking into account operators would make more than one element be generated and that's a bit too complex for this challenge. Maybe a future one. ;)

Input: A CSS selector, not including any operators. The element type will always be present, but the rest of the portions may not be.

Output: An html element representing the selector.


In: div
Out: <div></div>

In: div[with-data="cool-data"][foo]
Out: <div with-data="cool-data" foo></div>

In: span.nice.tidy.cool
Out: <span class="nice tidy cool"></span>

In: button#great-button.fancy[data-effect="greatness"]
Out: <button id="great-button" class="fancy" data-effect="greatness"></button>

In: custom-element#secret[secret-attribute="secret value!!"]
Out: <custom-element id="secret" secret-attribute="secret value!!"></custom-element>

In: element#id.class1.class2[attribute1][foo="bar"]
Out: <element id="id" class="class1 class2" attribute1 foo="bar"></element>
  • You may assume that the input selector will always have the different parts in order (element type, id, class, attributes).
  • You may assume that the provided attribute selectors will never be an attribute text search (^=, $=, *=).
  • You may assume that the input word characters (element type, attribute names, class names, ids) will always match the regex [a-z0-9_-]
  • You may have extra whitespace anywhere except for inside strings (attribute values)

This is , so the shortest answer (in bytes) wins. Standard loop-holes apply.

  • \$\begingroup\$ Are spaces valid input in the selector? I.e., div div.test => <div><div class="test"></div></div> \$\endgroup\$
    – Stephen
    May 26, 2017 at 1:26
  • \$\begingroup\$ No, I would count that as an operator. Would also make the challenge much harder. Maybe for a follow-up challenge ;) \$\endgroup\$ May 26, 2017 at 1:48
  • \$\begingroup\$ Can we aasume that the elements willonly be lowercase letters, digits, or '-'-? \$\endgroup\$ May 26, 2017 at 2:02
  • \$\begingroup\$ Yes, you may assume that. \$\endgroup\$ May 26, 2017 at 2:24
  • 1
    \$\begingroup\$ Second example's with-data attribute shouldn't be with=data in the output. \$\endgroup\$ May 26, 2017 at 10:59

3 Answers 3


PHP, 125 119 bytes

<?=preg_replace(['/#([\w-]+)/','/\.([^[]+)/','/[.[\]]/','/(\S+).*/'],[' id="$1"',' class="$1"',' ','<$1></$1>'],$argn);

Just regex replacements.
Saved 6 bytes thanks to Christoph.

  • \$\begingroup\$ How does this work for the last test case? \$\endgroup\$
    – Neil
    May 26, 2017 at 9:17
  • 1
    \$\begingroup\$ @Neil It works good Try it online! \$\endgroup\$ May 26, 2017 at 10:13
  • \$\begingroup\$ Aha, I see what it's doing now. (All those \s were confusing me.) \$\endgroup\$
    – Neil
    May 26, 2017 at 11:02
  • 1
    \$\begingroup\$ <?=preg_replace(['/#([\w-]+)/','/\.([^\[]+)/','/[.\[\]]/','/(\S+).*/'],[' id="$1"',' class="$1"',' ','<$0></$1>'],$argn); should work. \$\endgroup\$
    – Christoph
    May 30, 2017 at 13:25
  • 1
    \$\begingroup\$ It seems we don't need to escape [ within []: <?=preg_replace(['/#([\w-]+)/','/\.([^[]+)/','/[.[\]]/','/‌​(\S+).*/'],[' id="$1"',' class="$1"',' ','<$0></$1>'],$argn); another 2 bytes. \$\endgroup\$
    – Christoph
    May 30, 2017 at 13:32

Retina, 88 76 67 bytes


Try it online!

-10 bytes thanks to @Neil
-2 bytes thanks to @KritixiLithos

  • 1
    \$\begingroup\$ Your result is wrong, it should be class="class1 class-2". \$\endgroup\$
    – Neil
    May 26, 2017 at 9:16
  • \$\begingroup\$ @Neil fixed it. \$\endgroup\$
    – ovs
    May 26, 2017 at 9:32
  • \$\begingroup\$ That last $1$2 can be $&; your class check contains unnecessary ()s; also I think you can optimise some of those single-character replacements into a single T operation. (If not, ] doesn't need to be quoted.) \$\endgroup\$
    – Neil
    May 26, 2017 at 9:40
  • \$\begingroup\$ ^(\w+)(.*) can become ^(\w+).* because the second capturing group is not needed \$\endgroup\$
    – user41805
    May 26, 2017 at 11:18
  • \$\begingroup\$ ^(\w+).* can become (\S+).* (credits to @user63956). \$\endgroup\$
    – Christoph
    May 30, 2017 at 13:45

JavaScript (ES6), 193 bytes

s=>[[/#([\w-]+)/,` id="$1"`],[/((\.[\w-]+)+)/,(_,c)=>` class="${c.split`.`.join` `.trim()}"`],[/(\[.+?\])/g,(_,a)=>" "+a.slice(1,-1)],[/([\S]+)(.*)/,"<$1$2></$1>"]].map(r=>s=s.replace(...r))[3]

Longer than most since I preferred to output the HTML with all proper whitespace. Also works with the selector parts out of order (div[attr].class#id).

Test Snippet

s=>[[/#([\w-]+)/,` id="$1"`],[/((\.[\w-]+)+)/,(_,c)=>` class="${c.split`.`.join` `.trim()}"`],[/(\[.+?\])/g,(_,a)=>" "+a.slice(1,-1)],[/([\S]+)(.*)/,"<$1$2></$1>"]].map(r=>s=s.replace(...r))[3]

;["","div",'div[with-data="cool-data"][foo]',"span.nice.tidy.cool",'button#great-button.fancy[data-effect="greatness"]','custom-element#secret[secret-attribute="secret value!!"]','element#id.class1.class2[attribute1][foo="bar"]'].forEach(s => S.innerHTML += `<option>${s}</option>`);
Test Cases: <select id="S" onchange="I.value=this.value,I.oninput()"></select><br><br>
<input id="I" oninput="O.innerText=f(this.value)" size="75">
<pre id="O"></pre>


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.