Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overwriting tag names #55

Open
andreyvolokitin opened this issue Dec 31, 2017 · 16 comments
Open

Overwriting tag names #55

andreyvolokitin opened this issue Dec 31, 2017 · 16 comments
Assignees

Comments

@andreyvolokitin
Copy link

Docs says:

You can inject locals into any piece of content in your html templates, other than overwriting tag names

Is it fundamentally impossible or just not implemented currently?

@Scrum Scrum self-assigned this Jan 5, 2018
@Scrum
Copy link
Member

Scrum commented Jan 5, 2018

@andreyvolokitin Hi, this should work, show an example that does not work for you.

@andreyvolokitin
Copy link
Author

Will do a repo later if necessary, just checked — tag name indeed is not overwritten in the final output while other expressions are overwritten: https://i.imgur.com/bEVfwmq.png (final output shown in the bottom left corner)

@andreyvolokitin
Copy link
Author

Yeah, expression like this:

<{{tag}} type="{{type}}" class="button">
    <i class="button__icon"></i>
    <span class="button__text">{{text}}</span>
</{{tag}}>

with a posthtml.config.js like this:

module.exports = {
  plugins: [
    require('posthtml-expressions')({
      locals: { tag: 'a', text: 'Button', type: 'submit' }
    }),
  ],
};

will produce the following output:

<{{tag}} type="submit" class="button">
    <i class="button__icon"></i>
    <span class="button__text">Button</span>
</{{tag}}>

@andreyvolokitin
Copy link
Author

@jescalan could you explain this? I see this detail about tags was initially added in the readme by you (3d04646#diff-04c6e90faac2675aa89e2176d2eec7d8R35). Is this because expressions themselves are made via custom tags?

@andreyvolokitin
Copy link
Author

I assume that is the case. The only question is — is it fundamentally impossible to overwrite node.tag? I.e. after all tag-logic is handled, could it be possible to check if node.tag contains expression and handle it?

@jescalan
Copy link
Contributor

I would say its theoretically possible, but incredibly difficult to implement, and I have yet to see a use case that would validate the effort.

In order to implement this, the parser would need to be modified to recognize <{{ .* }}> as a tag, which no current html parser supports (and reasonably so), which means forking the base parser or implementing a custom one. On top of that, since the delimiters can be changed, and often are (when working with vue.js, for example), the configuration for this specific plugin would need to flow back upstream to the parser and change it's core behavior, coupling the two in a way that is very much undesirable for a plugin-based architecture. Also, if someone elects to use a different parser (like sugarml for example), that parser would also need to support this feature that is contained in this specific plugin. Finally, it would make it not only possible, but easy for users to produce invalid html via mismatched tags.

If you have a specific use-case that really needs this feature, by all means I'd be curious to see it laid out here. There is potentially an alternative way to produce the same effect that doesn't require such a huge amount of work and re-architecting the system!

@andreyvolokitin
Copy link
Author

I am trying to set up a frontend dev environment based on webpack which, amongst other things, would allow me to reuse HTML pieces. I am looking for something like a templating engine to use only at the development stage, with a decent level of flexibility, so I could potentially parametrize as much as I can in an HTML component, including tag names (though maybe I will be using mainly a few features, it is nice to have them when you need them). So this is my use-case.

There are a surprisingly low number (actually 0, AFAIK) of webpack loaders for templating engines which work fully and reliably within webpack ecosystem in my usecase. PostHTML is by far the best option I found for integrating with webpack, but it is not a templating engine per se. So I am trying to understand what are the limits with PostHTML.

Probably I could use simple logic to switch between different HTML pieces so that I will not need to rewrite tag names, but that is not quite the same as "tag rewriting"...

In order to implement this, the parser would need to be modified to recognize <{{ .* }}> as a tag

I tested this by outputting node.tag in the console within a walk() function and it correctly printed tags like this: {{tag}}. Then I added the following on this line:

if (String.prototype.indexOf.call(node.tag, '{{') === 0) { //just a hack
    node.tag = placeholders(node.tag, ctx, delimitersSettings)
}

and used this html:

<{{tag}} type="{{ type }}" class="button">
  <i class="button__icon"></i>
  <span class="button__text">{{ text }}</span>
</{{tag}}>

and everything worked: the tag was replaced, as well as other variables, output was generated without errors. BUT outupt is broken if I put spaces after {{ and/or before }} within the tag placeholder. What do you think?

@jescalan
Copy link
Contributor

Yeah, I guess I just have never run into a case where deciding a tag name based on external input would be reasonable or safe in any way. I still don't really understand what your use-case is, to be honest. Like, in what situation do you need the name of the tag itself to be represented by a variable? If you can lay this out, it's possible I could see a different approach that would save you the effort of a hard fork.

The way I see it, even if this were able to be implemented, this would be an extremely dangerous, and very infrequently used feature, so I want to make sure that there is a very valid use case before getting into the possibility of adding it.

That being said, your investigation so far is solid, it seems like if it were to be added that would be a good way to do it that I hadn't thought of previously!

@andreyvolokitin
Copy link
Author

Having some HTML-"components" library, there may be times when the root or some other tag within the component should be variable. I.e. with a button component — it can be a real <button>, or an <a>, or just a <div>, the basic structure remains the same:

<{{tag}} class="button">
  <i class="button__icon"></i>
  <span class="button__text">{{ text }}</span>
</{{tag}}>

Less granular example: having some block (i.e. product block with a picture, description and a "buy" button), it can be a <div>, or it can be made clickable by using an <a> instead.

This is not so common in general, but there are cases when this is very handy

@andreyvolokitin
Copy link
Author

The way I see it, even if this were able to be implemented, this would be an extremely dangerous, and very infrequently used feature

It can be hidden behind an option, and if it works correctly then won't this be no more dangerous than just manually writing messed up HTML, like having divs directly within tables? Writing sane HTML may be user's responsibility, and they need to do it even before they start using PostHTML

@jescalan
Copy link
Contributor

Ok, I can see that case for sure. I mean, this could be very easily worked around with an if/else statement for this particular one. Or just by just using an <a> no matter what, to be honest, and just not giving it an href or click action if it doesn't have one. Are there any other use cases you have run into other than this a or button/div case? Is there any drawback that you know of to using an a without an href? According to the html spec, it is perfectly valid markup, and considered a "placeholder hyperlink".

@andreyvolokitin
Copy link
Author

if/else means code duplication, which negates "components" approach, where there is a single source of "truth". Regarding href — I am really not sure, but it sounds like asking for trouble (maybe I am paranoid, but I dealt with quite a lot of pretty bizarre browser bugs to start fearing of anything "special"). All in all, workarounds can be done, but they are just that — workarounds. For further examples, I will browse my projects where I reused the same markup but with different tags, this will take some time. But they also may be "workaroundable". The thing is that this feature may be usable, and it may be optional — so if it can be implemented relatively easy and would benefit even just a few, why not implement it? I can volunteer, as far as I am qualified

@andreyvolokitin
Copy link
Author

Ah, and regarding always using <a> for buttons — I am afraid this would be impossible, because the most common thing is to use <button> for buttons, so that they work even without JS, and so they are "semantic" etc. It is just sometimes when you need to use <a> (or another tag) instead, but the markup is just the same

@jescalan
Copy link
Contributor

jescalan commented Jan 14, 2018

if/else means code duplication

Absolutely true, but if it is two lines that are duplicated in a single component, it can honestly end up being a non-issue, and not worth burning a large amount of time into an in-depth PR to avoid.

the most common thing is to use for buttons, so that they work even without JS

Could you elaborate on this? In what situation would a <button> element be able to do something that an <a> couldn't, without JS active? And I'm not sure it matters how common something is - in this case I'm after actual practical differences.

@andreyvolokitin
Copy link
Author

andreyvolokitin commented Jan 15, 2018

but if it is two lines that are duplicated in a single component, it can honestly end up being a non-issue, and not worth burning a large amount of time into an in-depth PR to avoid.

It can, or it can not, depending on the situation and quantity of such components. In larger components, it definitely will be sort of a bad thing. One thing for sure: the useful feature will be absent

Could you elaborate on this? In what situation would a element be able to do something that an couldn't, without JS active?

Like in forms, where you actually need <button>, particularly a submit or reset button. Why would you use an <a> in a place for <button> in a form anyway? The button is a desired mechanism for doing all "button stuff" outside forms either (also see: https://www.nczonline.net/blog/2013/01/29/you-cant-create-a-button/), but in forms it is required.

@andreyvolokitin
Copy link
Author

Okay, I looked quickly into my projects, and apart from button example, that's what I found:

  • a heading block, which can contain some markup inside, and you can reuse it with different heading levels:
<{{tag}} class="header">
    <span class="header__text">Heading text</span>
    <span class="header__misc">misc stuff</span>
</{{tag}}>
  • a "banner/info" block, which can contain some markup inside, and you can make it a plain <div>, or an <a> to make a link somewhere:
<{{tag}} {{attrs}} style="background-image: url(img/ban.jpg); min-height: 126px;" class="ban clearfix">
    <div class="ban__side">
        <div class="ban__logo">
            <img src="img/logo.jpg" alt="" width="154" height="30" class="ban__logo-img"/>
        </div>
        <div class="ban__slogan">because we love you</div>
    </div>
    <div class="ban__desc">
        <h3 class="ban__head">Only in October</h3>
        <p>mad sale on donuts!</p>
    </div>
</{{tag}}>
  • even with a small button component, it can be not so small and even a single line can contain a lot of stuff which with if/else you got to keep in sync:
<{{tag}} some="some" required="required" attributes="attributes" class="button something js-button">
  <i class="button__icon"></i>
  <span class="button__text">{{ text }}</span>
</{{tag}}>

Indeed it may be a not-so-practical feature, probably it could be worked around. Like I said before: it is not so common, but when it is needed it can be quite handy. Maybe @voischev and @awinogradov could mention more use-cases, or confirm it is not worth it. Personally, I think that it won't hurt to add it as there are valid use-cases, and as it seems like it is already sort of works out of the box

andreyvolokitin added a commit to andreyvolokitin/posthtml-expressions that referenced this issue Feb 4, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants