Global Area Stacks with HTML Blocks cause Duplicate Block ID errors after the initial reference ... is there a workaround?

Permalink
I've got a series of HTML blocks in a global area stack in the footer and every validator I've used to check the code is saying that there is a "Duplicate ID HTMLBlock522" error. It doesn't like the fact that Stacks used in Global areas utilize the same Block ID issued in the initial instance of the block. It wouldn't be an issue if it were in a standard, non-global area but since it is in a global area in the footer, it is simply repeating the same Block ID over and over on every page and all but the initial ID seen on the Home Page is considered an error in the code. Is there a workaround for this?

http://www.i17tbirdstorage.com/...

https://validator.w3.org/nu/?doc=http%3A%2F%2Fwww.i17tbirdstorage.co...

barkingtuna
 
Gondwana replied on at Permalink Reply
Gondwana
I think the problem is more general and insidious than even this. If you copy and paste any block, c5 will use the same block ID for both element IDs. This gets it into trouble when trying to keep track of copied forms on a page, since they both respond to submissions made to either one of them.

I suspect c5 should be using the proxyBlock->bID where available, instead of element ID. I don't think this will be easily fixed.
barkingtuna replied on at Permalink Reply
barkingtuna
Thanks... I was afraid of that.
JohntheFish replied on at Permalink Best Answer Reply
JohntheFish
This used to be a common minor bug through much of the core and still exists widely in 5.6. 5.7 has cleaned up most instances, but not all. Places the mistake remain include the html block and the form helper. Its a misguided programming convenience.

For your current problem with the html block, just create a custom template without the wrapper or with an #id free wrapper. The #id is not used for anything.

As a general rule, never use bID to generate an html/css #id because, as already described, it won't be unique.
If an #id is really needed, there are several better alternatives that can be used to ensure an ID is unique when the view is rendered.
- The unique ID helper
- block proxy ID
- A simple random generator (which is part of what happens inside the unique ID helper)

Using a non-unique bID as an html #id is such a common mistake made by new block developers that perhaps the base class for blocks should have a getUniqueID() method, so any block can simply inherit and use that method.

For block CSS and jQuery selection, better to use classes and other selectors where possible.

There are still times when a view will need to use the actual bID, such as connecting back to the correct block controller during an ajax action or form submission. But not for an html #id.
Gondwana replied on at Permalink Reply
Gondwana
The problem seems to exist in the c5.7 documentation too; eg:
http://documentation.concrete5.org/developers/working-with-blocks/c...
JohntheFish replied on at Permalink Reply
JohntheFish
In that example, I don't see the bID being used as an html #id attribute.
barkingtuna replied on at Permalink Reply
barkingtuna
@JohntheFish... it's there. Here are some examples of the initial instance and pages showing the same HTMLBlock523 ID. I will give the Template suggestion a shot. Thanks for the suggestions and info from you both!

1. Initial instance of the HTMLBlock 523 is at http://www.i17tbirdstorage.com
Line 536: <div id="HTMLBlock523" class="HTMLBlock">

All future instances are in a stack in a Global Area in the Footer

2.http://www.i17tbirdstorage.com/self-storage-rates-phoenix-az-85023...
Line 800: <div id="HTMLBlock523" class="HTMLBlock">

Excerpt from Validator:
Error: Duplicate ID HTMLBlock523.
From line 804, column 1; to line 804, column 41
↩ ↩↩ ↩<div id="HTMLBlock523" class="HTMLBlock">↩<div
Warning: The first occurrence of ID HTMLBlock523 was here.
From line 301, column 1; to line 301, column 41
↩ ↩↩ ↩<div id="HTMLBlock523" class="HTMLBlock">↩<div

3. An additional example http://www.i17tbirdstorage.com/self-storage-tips-phoenix-az-85023/s...
Line 770: <div id="HTMLBlock523" class="HTMLBlock">

Excerpt from Validator:
Error: Duplicate ID HTMLBlock523.
From line 774, column 1; to line 774, column 41
↩ ↩↩ ↩<div id="HTMLBlock523" class="HTMLBlock">↩<div
Warning: The first occurrence of ID HTMLBlock523 was here.
From line 301, column 1; to line 301, column 41
↩ ↩↩ ↩<div id="HTMLBlock523" class="HTMLBlock">↩<div
JohntheFish replied on at Permalink Reply
JohntheFish
I agree with all your examples (as noted in my first post to this thread) and can cite many more.

My immediately above comment was referring to the reference to
http://documentation.concrete5.org/developers/working-with-blocks/c...
barkingtuna replied on at Permalink Reply
barkingtuna
FYI... the Template solution worked brilliantly! Thank you!

Now, if I could only figure out how to clean up some of these other add-ons by extracting the dynamically generated <style> elements inserted between DIVs and putting them in an external CSS file or in the header. I'm just venting at this point, not asking for help, but it keeps saying that I can't have <style> in the middle of the page. Once I figure this issue out, life will be great and the site would be "error" free! :)

Thanks again for the HTML Block fix!
JohntheFish replied on at Permalink Reply
JohntheFish
A trick I have used many times if for my block controller's on_page_load handler to generate the block specific styles and insert them into the page header.

All depends on how much re-engineering of other blocks you want to do.

You could also look at "miser" (not in the marketplace). It runs just before a page is output and, amongst other things, can consolidate inline <style> tags into the header. I don't know if it is 5.7 compatible.
https://www.concrete5.org/community/forums/customizing_c5/miser-web-...
barkingtuna replied on at Permalink Reply
barkingtuna
I just extracted the styles and modified the various package files to achieve the same... seemed easier. Thanks again for your suggestions. They really helped as you can see... the site went from a 6.8 overall to a 9.2 and a "Code Quality" score of 0.0 to 9.8 now!

https://bdc.silktide.com/en/reports/www.i17tbirdstorage.com...
Gondwana replied on at Permalink Reply
Gondwana
@John: Re "block controller's on_page_load handler", is this really a thing? I can't get it to fire, nor can I find any documentation on it.

I'd love to be able to use it (or equivalent) to be able to inject some code into a page once only, rather than once for every instance of my block; eg, translated string constants.
JohntheFish replied on at Permalink Reply
JohntheFish
typo on my behalf. The usual event for setting up headers from within a block is on_page_view.
http://documentation.concrete5.org/developers/appendix/full-event-l...

Within that you can do things like (pseudocode)
$this->addHeaderItem('<style>a list of block specific styles</style>');

Its the html helper that prevents duplication of headers/footers
$this->addHeaderItem($html->javascript('javascript to insert directly'));


For translated strings in a block that need to be passed to javascript, there is an il8n method you can add to a block controller and the tagged strings in it get translated and inserted into a page header. I don't think there are any 5.7 docs on that. You may be able to find something in 5.6, or you will need to search through the core for blocks that provide examples.
Gondwana replied on at Permalink Reply
Gondwana
Thanks! I hadn't discovered that list of events.

I've spent probably 10 hours looking for doco or code on a general JS way to translate strings, and failed. The closest thing I could find was ccm_t, but that isn't defined within a block's view.php.
JohntheFish replied on at Permalink Reply
JohntheFish
Just did a trawl through the core BlockController class that blocks inherit from. The method you want is getJavaScriptStrings(), last in the file.

If you search through the core for uses of that, in in rss-display is an example with 2 strings
public function getJavaScriptStrings()
    {
        return array(
            'feed-address'   => t('Please enter a valid feed address.'),
            'feed-num-items' => t('Please enter the number of items to display.')
        );
    }

(and others)

The core sets these up as hidden inputs, with a name prefix. They can be retrieved in JS using the ccm function
ccm_t('feed-address');


The core provides all that in add/edit mode. You may need to call something to get the strings included in a normal page view mode.
Gondwana replied on at Permalink Reply
Gondwana
Yes, I've tried ccm_t() but, as you say, it only works in add/edit. I can't understand how the use of hidden fields applies in my situation since I'm not using the strings in forms. Other references I've found:
https://www.concrete5.org/community/forums/customizing_c5/translatio...
and
https://www.concrete5.org/community/forums/customizing_c5/howto-tran...
...neither of which ends happily. The fallback always seems to do it in php; eg,
https://www.concrete5.org/community/forums/customizing_c5/javascript...

I'll have a closer dissect of getJavaScriptStrings() and rss-display tomorrow.

I remain unclear about the advantage of using this approach (which uses t() in the controller) instead of using t() in view.php. In either case, t() is used. Can c5 not see uses of t() in view.php?
JohntheFish replied on at Permalink Reply
JohntheFish
The hidden form elements are purely a way c5 uses to embed the strings and could be used without an actual form, though when used with a form it does provide scope. In 5.6 they were embedded as global javascript constants.

t() works perfectly well in view.php. Other approaches to passing strings include simply echoing php directly into javascript in the view, or attaching them as data attributes to a dom element, either individually or as json. You need to take care to properly escape such strings so nothing gets broken, especially when minification could be applied.
Gondwana replied on at Permalink Reply
Gondwana
Thanks heaps, John. That gives me a lot of options. Point taken about escaping and minification; I've been a bit casual about that.