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 '17 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\$ – Mackenzie McClane May 26 '17 at 1:48
  • \$\begingroup\$ Can we aasume that the elements willonly be lowercase letters, digits, or '-'-? \$\endgroup\$ – pizzapants184 May 26 '17 at 2:02
  • \$\begingroup\$ Yes, you may assume that. \$\endgroup\$ – Mackenzie McClane May 26 '17 at 2:24
  • 1
    \$\begingroup\$ Second example's with-data attribute shouldn't be with=data in the output. \$\endgroup\$ – Gábor Fekete May 26 '17 at 10:59

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 '17 at 9:17
  • \$\begingroup\$ @Neil It works good Try it online! \$\endgroup\$ – Jörg Hülsermann May 26 '17 at 10:13
  • \$\begingroup\$ Aha, I see what it's doing now. (All those \s were confusing me.) \$\endgroup\$ – Neil May 26 '17 at 11:02
  • 1
    \$\begingroup\$ <?=preg_replace(['/#([\w-]+)/','/\.([^\[]+)/','/[.\[\]]/','/(\S+).*/'],[' id="$1"',' class="$1"',' ','<$0></$1>'],$argn); should work. \$\endgroup\$ – Christoph May 30 '17 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 '17 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 '17 at 9:16
  • \$\begingroup\$ @Neil fixed it. \$\endgroup\$ – ovs May 26 '17 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 '17 at 9:40
  • \$\begingroup\$ ^(\w+)(.*) can become ^(\w+).* because the second capturing group is not needed \$\endgroup\$ – Kritixi Lithos May 26 '17 at 11:18
  • \$\begingroup\$ ^(\w+).* can become (\S+).* (credits to @user63956). \$\endgroup\$ – Christoph May 30 '17 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, privacy policy and cookie policy

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