Problem/Motivation
When a page contains a form and a fragment with special characters is in the url, such as http://mysite.com/#badfragment=broken, a javascript error will be thrown: Uncaught Error: Syntax error, unrecognized expression: .error, #badfragment=broken.
This issue happens in form.js near line 300:
const hash = url.hash.substring(1);
if (hash) {
const $target = $(`#${hash}`);
Notice that the anchor is being directly pulled from the URL and used in the selector. However, `#${hash}` will not yield a valid selector, resulting in an error being thrown (breaking all JS on the page).
Steps to reproduce
Add this link to a page <a href="#edit-contact/broken">This is an anchor link with a broken fragment</a> then click it, and the error will appear in the console.
Alternatively visit https://local.site/user/register#badfragment=broken on a site.
Proposed resolution
Provide a helper that converts a fragment into a safe ID that can be used for a jquery or other selector.
Remaining tasks
Need help writing tests that assert no console errors visiting form urls with complex fragments?
Review
User interface changes
n/a
API changes
n/a
Data model changes
n/a
Release notes snippet
n/a
| Comment | File | Size | Author |
|---|---|---|---|
| #57 | Screen Recording 2024-06-18 at 11.38.14 AM.mov | 2.25 MB | yash.rode |
| #52 | 2395065-52.patch | 1.04 KB | adinancenci |
| #46 | 2395065-46.patch | 1.04 KB | _utsavsharma |
| #46 | interdiff_40-46.txt | 1.04 KB | _utsavsharma |
| #40 | 2395065-40.patch | 3.84 KB | callen321 |
Issue fork drupal-2395065
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #1
rooby commentedA work around that might work for your case is this:
Instead of
Use this
Comment #2
samhassell commentedThis conflict comes up when using AngularJS's $location service within D7.
Sample URL:
Comment #3
polSame here too.
Comment #4
polIn misc/collapse.js, replace:
With:
If I get good feedback, I'll propose a patch for D7 core.
Comment #5
polHere's the patch.
Comment #6
tim.plunkettThis code is unchanged in D8, meaning that it should probably be fixed there first.
Comment #7
polHere's the patch for D8.
Comment #8
longwaveIs
[=%;,\/]the full set of invalid characters, or could there be others? If a replacement takes place, will it ever match anything useful, or match something else by mistake?Maybe this is more readable/useful:
if (anchor.search(/[=%;,\/]/) == -1 && ...) {or
if (anchor.search(/^[valid characters]*$/) != -1 && ...) {Comment #9
markhalliwellThere could be others.
I would much rather the route of checking for expected/valid characters as is done in
https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Component%21Util...
That being said, I would imagine that we could abstract this and just add it to drupal.js as a utilitarian method:
Then we could just do something like:
var anchor = Drupal.cleanId(location.hash && location.hash !== '#' ? ', ' + location.hash : '');Comment #10
sgdev commentedThank you for everyone's work on this issue. A more generic approach is definitely the way to go. We ran into this problem with AddThis. AddThis adds anchor tags on URLs when recommended content links are clicked. The rendered URL displays something like the following:
The #5 patch appropriately handles the equal character ("="), but does not replace the ampersand ("&"). The result is a jQuery error:
If we add "&" to the possible characters in Patch #5 it works correctly. Therefore a patch that handles any possible special symbol would be the best method.
Comment #11
David_Rothstein commentedI just marked #2651230: collapse.js breaks when the hash contains jQuery BBQ-like query string as duplicate. There is a patch in that issue also.
Comment #16
markhalliwellBump, re: #9
Comment #19
geek-merlinThis issue always arises when hashes are not escaped. I have been facing it when blazy_photoswipe generated URLs like "foo#&gid=1&pid=2", and form.js's handleFragmentLinkClickOrHashChange() (see #2531700: Fragment links to children elements in closed grouping elements don't work) coughed on that:
the offending line in form.js:137 is
and replacing that (like in the patch here) with
fixes the issue for me.
For a thorough fix we should implement a utility method as suggested in #9 and use it in all such instances.
For the correct utility method see:
* http://api.jquery.com/category/selectors/ ("meta characters...must be escaped")
* https://stackoverflow.com/a/32872718/606859
* https://github.com/mathiasbynens/CSS.escape/blob/master/css.escape.js#L21
The last one contains a MIT licensed function that polyfills CSSCOM's CSS.escape which is what we want here.
Comment #20
batkor#19 +.
I'm get error in form.js
Axel.rutz Ty
Comment #21
batkorpatch for form.js
Comment #22
andypostAs bug it needs test but let's see current coverage
Comment #23
batkori think #9 true.
And use for form.js, collapse.js
Comment #25
aklump commentedThank you, batkor, #21 patch worked for me. 8.6.10.
Comment #26
bmunslow commentedPatch #21 worked for me, thanks @baktor.
Comment #27
markhalliwellSee #9
Comment #30
neclimdulUgh, ran into this randomly breaking a embedded Vue SPA.
Similar I think to longwave's comment about earlier patch I think but it doesn't make sense that this triggers when there is no match. It wouldn't really be actionable by any listeners without a target would it? That's a bigger change though and just and optimization.
I took a pass at implementing Mark's helper since that makes sense with 2 locations. With 2 there's probably a third. His sample looked correct so I didn't really change anything other than some small cleanups:
1. chaining it
2. replaced the first regex with a native regex
3. added global flag to regex because they where only matching the first instance.
I don't have a lot of experience writing core's JS tests so those aren't included but as a first pass lets see if this breaks anything.
Had to reroll because of babel changes(+ to .concat) and that's not included in interdiff. Also didn't interdiff against the previous collapse patch since it was not on the es6.js file and that meant more work but changes should be obvious.
Comment #33
neclimdulReviewing some old patches I noticed some doc/spacing problems in the last patch. Quick fix.
Comment #34
neclimduleslint
Comment #38
callen321 commentedremoved
Comment #39
callen321 commentedremoved
Comment #40
callen321 commentedRerolled for 9.5
Comment #41
callen321 commentedComment #42
neclimdulcan't rtbc what's basically my own patch but we've been using this in production for _ages_ so it should be good to go.
Comment #43
geek-merlinWe fix a bug, so needs a red test that triggers the bug and is fixed by the patch.
Comment #44
quietone commentedThis also needs an issue summary update. The proposed resolution should explain what solution is being implemented.
Comment #45
longwaveAlso needs a reroll for 10.x. Bug fixes are usually eligible for backport but in this case we are adding API so this is likely to land in the development branch only.
Comment #46
_utsavsharma commentedPatch for 10.1.x.
Comment #47
sahil.goyal commentedComment #48
longwaveThanks for rerolling. This still needs a test to exercise the broken behaviour and prove that we have fixed it with the patch.
Comment #51
neclimdulconflicts with #3419763: Replace deprecated String.prototype.substr() with String.prototype.substring(). rerolled in a merge request. not sure what a test would look like so just the reroll atm.
Comment #52
adinancenci commentedPatch 46 adapted for Drupal 10.2.4.
Comment #53
jmaguniaThe patch from #52 applied cleanly to Drupal 10.2.5.
Comment #55
yash.rode commentedHi, I was trying to reproduce this issue in Drupal Core, where I can click on any button which takes me to the link with a fragment?
I am trying to write the test, but not sure what should be the Drupal page where I can get this error which I can convert to test.
Comment #56
neclimdulNot sure of a common real case but I was able recreate this just now like this.
1. Install Drupal site.
2. Ensure registration is allowed.
3. Log out and visit the registration url with a complex url fragment. e.g. https://d10.lndo.site/en/user/register/#badfragment/broken
Comment #57
yash.rode commentedHi I tried the steps above but the issue only occurs when we try to change the URL and not when we visit the URL for the first time?
If that is the expected behaviour how can we write a test for that scenario?
Comment #58
bnjmnmTesting / reproducing is fortunately pretty simple. The callback where the problem is occuring is triggered by, among other things, clicking on an anchor link inside a form. Get this link into a form
<a href="#edit-contact/broken">This is an anchor link with a broken fragment</a>then click it, and the error will appear in the console.And hopefully the issue summary can get updated soon as it is presents the issue as being specific to a file that has not been in Drupal for several years.
Comment #59
neclimdulThanks, yeah adding that link would be a simple way someone could see it.
Anyone could update issue summaries.
Comment #60
utkarsh_33 commentedI tried reproducing this based on #58.
I added
in
FormTestUrlFormform and clicked on the anchor link inside the form.This does not lead to any error on 11.x.Also it tried to edit the url and hit the url as mentioned in #57 by @yash.rode but then also there is no error in the console.
Also when following the same steps as mentioned by @yash.rode in #57 I was able to reproduce the same error as yash was able to get.
The question here is am I missing something is steps to reproduce?
Comment #61
utkarsh_33 commentedComment #62
neclimdulI think this is documented and reviewed to death at this point. Its actually needs work because it needs tests as the tag suggests.
Comment #63
bnjmnmRe #60
Yes. The file where the error is happening needs to be loaded in order to reproduce the problem.
The error is occurring in
form.js, but the form loaded byFormTestUrlFormdoes not include that file.See core.libraries.yml for the library that includes this file and the libraries that depend on it.
Comment #65
budda11 years ... wow, and still not resolved. 🙄
Is there any reason why jQuery.escapeSelector() wasn't used insteadof the cleanId function @Utkarsh_33 ?
https://api.jquery.com/jQuery.escapeSelector/