How to detect text fragments `#:~:text=` with JS? - javascript

when a user clicks on a featured snippet in Google search results, the text element is highlighted by a text fragment. The browser address looks like example.com/landing-page#:~:text=Start%20of%20fragment%20an,End%20of%20fragement.
I now want to detect with JavaScript if a user visits the page with a highlighted text.
I already tried window.location.hash and document.URL but the value after # is not part of the result.
Is that possible with pure JS?

As far as I know, there is no clean and reliable way to do this.
But it's simple to remove this text-fragment hash and highlighting (in typical cases). All what you need is this:
if(/\.google\....?\/$/.test(document.referrer)) {
location.hash= "top";
history.pushState({}, "", location.pathname);
}
Detection of text-fragments is possible in cases, where the target text is "below the fold" and therefore the page scrolled down to make it visible. Example code:
onload= function() {
if(scrollY) {
alert("page has scrolled down likley because of text-fragment link");
// do what you need here
}
}
As an ugly hack, you can force this scrolling by moving all content below the fold and resetting afterwards. Something like this (code has room for improvement):
if(/\.google\....?\/$/.test(document.referrer) {
// add margin to <body> - move content down to force scrolling
document.body.style.marginTop= "111vh";
onload= function() {
if(scrollY) {
alert("page has scrolled down likley because of text-fragment link");
// do what you need here
}
// reset margin
document.body.style.marginTop= "";
}
}

Related

Preventing textarea scroll behaviour in chrome after newline added

Recently my version of chrome has been doing something strange (74.0.3729.131 on ubuntu 18.04) more and more often. I have a small editor script which has a textarea which displays code. The textarea has a fixed size and a vertical scroll bar. Beyond that nothing fancy.
Usually, when I insert a newline (normal behaviour of textarea), the scroll bar doesn't move. Now for some reason about 80% of the times it scrolls the textarea down till the position of the caret is at the top of the textarea. Strangely if I delete and enter the newline in the same position, it usually does not scroll.
I'm not sure if this is some new issue in Chrome. I usen't have this issue with previous versions with the identical editor.
Here is a codepen which demonstrates the issue, scroll to some line, press enter and the textarea should scroll down. Try this a few times to see the unpredictable behaviour (adding the code just to be able to add the link, as you can see it's just a textarea).
https://codepen.io/anon/pen/rgKqMb
<textarea style="width:90%;height:300px"></textarea>
The only solution that occurs to me to avoid this is to stop the normal behaviour of the enter key and add the newline to the text. Any other ideas/insights very much welcome.
It's almost the end of 2020, Chrome version 86 and this issue still exists? What's more, I am surprised I have not found more information (complaints) on this matter (this post is the only thing I've found which speaks of this issue specifically.) I have observed that this behavior occurs not only in typing, but pasting any text containing a newline. I also observed that if I execute an undo action after this occurs, another random scroll happens, taking me even farther up the page, and nowhere near where the caret is.
I experimented and examined this behavior at much length, and was not able to find any repeatable circumstances which might give a clue as to how to predict when this would occur. It truly just seems "random". Nonetheless, I've had to work around this issue for an NWJS editor app I'm creating (NWJS uses Chrome for UI.)
This is what seems to be working for me:
First all, let me start simple in order to introduce the principle. We attach an "input" listener and "scroll" listener to the textarea. This works because, from my observation anyway, the "input"[1] listener gets fired before the random scroll action occurs.
The scroll listener records each scrolling action and saves it in a global prevScrollPos. It also checks for a global flag scrollCorrection.
The "input" listener sets the scrollCorrection flag everytime text is input into the textarea. Remember, this has happened before the random scroll occurs.
So the next scroll to occur, which may be the nuisance random action, the scroll listener will clear scrollCorrection, then scroll the textarea to the previous scroll position, ie, scrolling it back to where it was before the "random" scroll. But the issue is unpredictable, what if there is no random scroll and the next scroll to occur is intentional? That is not a big deal. It just means that if the user scrolls manually, the first scroll event is basically nullified, but then after that (with scrollCorrection cleared) everything will scroll normally. Since during normal scrolling, events are spit out so rapidly, it is unlikely there will be any noticeable effect.
Here is the code:
let textarea;
let prevScrollPos = 0;
let scrollCorrection = false;
function onScroll(evt) {
if (scrollCorrection) {
// Reset this right off so it doesn't get retriggered by the corrction.
scrollCorrection = false;
textarea.scrollTop = prevScrollPos;
}
prevScrollPos = textarea.scrollTop;
}
function onInput(evt) {
scrollCorrection = true;
}
window.addEventListener("load", () => {
textarea = document.getElementById("example_textarea");
textarea.addEventListener("scroll", onScroll);
textarea.addEventListener("input", onInput);
})
Now let's expand on it:
There is another consideration. What if the typing or pasting action puts the end of the typed or pasted text (and thus the caret) outside the view of the textarea viewport? When normal scrolling is in play, most browsers will scroll the page[2] so the caret will remain in view. However now that we've taken over scrolling action, we'll need to implement that ourselves.
In the psuedo-code below, on input to the textarea, besides setting scrollCorrection, we call a function which will:
determine the xy position of caret relative to textarea viewport
determine if it is scrolled out of view
if so:
determine the amount to scroll to bring it in view
determine if the random scroll has already occurred by testing the state of scrollCorrection
if it hasn't, set flag scrollCorrection2 containing the amount to scroll
if it has, explicitly do the additional scrolling to bring it back into view
Finding the xy position of the caret in a textarea is not a trivial matter and is outside the scope of this answer, but there are plenty of methods to be found in searching the web. Most involve replicating the textarea contents in a non-form element, eg div block, with similar font, font-size, text wrapping etc, then using getBoundingClientRect on the resultant containing block and such. In my situation, I was already doing most of this for my editor, so it wasn't much of an additional expense. But I've included some psuedo-code to show how this can be implemented in the scroll correction mechanism. setCaretCorrection basically does steps 1 - 7 above.
let textarea;
let prevScrollPos = 0;
let scrollCorrection = false;
let caretCorrection = 0;
function onScroll(evt) {
if (scrollCorrection) {
// Reset this right off so it doesn't get retriggered by the correction.
scrollCorrection = false;
textarea.scrollTop = prevScrollPos + caretCorrection;
caretCorrection = 0;
}
prevScrollPos = textarea.scrollTop;
}
function onTextareaInput() {
scrollCorrection = true;
setCaretCorrection();
}
function setCaretCorrection(evt) {
let caretPos = textarea.selectionStart;
let scrollingNeeded;
let amountToScroll;
/* ... Some code to determine xy position of caret relative to
textarea viewport, if it is scrolled out of view, and if
so, how much to scroll to bring it in view. ... */
if (scrollingNeeded) {
if (scrollCorrection) {
// scrollCorrection is true meaning random scroll has not occurred yet,
// so flag the scroll listener to add additional correction. This method
// won't cause a flicker which could happen if we scrollBy() explicitly.
caretCorrection = amountToScroll;
} else {
// Random scroll has already occurred and been corrected, so we are
// forced to do the additional "out of viewport" correction explicitly.
// Note, in my situation I never saw this condition happen.
textarea.scrollBy(0, amountToScroll);
}
}
}
One could go further and use the experimental event, "beforeinput"[3], to optimize this a little bit so fewer unnecessary calls to setCaretCorrection are made. If one examines event.data from "beforeinput" event, in certain cases it will report the data to be input. If it does not, then it outputs null. Unfortunately, when a newline is typed, event.data is null. However it will report newlines if they are pasted. So at least one can see if event.data contains a string, and if the string does not contain newlines, skip the whole correction action. (Also, see [1] below.)
[1] I also don't see any reason you couldn't do in the "beforeinput"[3] listener, what what we're doing in the "input" listener. That may also give more insurance that we set scrollCorrection before the random scroll occurs. Although note that "beforeinput" is experimental.
[2] I suspect it is broken implementation of this feature which is causing this issue.
[3] https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/beforeinput_event (Available on Chrome and all major browsers except Firefox according to this link.)
You can try avoiding the events on the textarea with css and js, then force the scroll to it's current position:
css:
textarea {
overflow:auto;
resize:none;
width:90%;
height:300px;
}
js:
You'll need to insert the first answer from this question at A
function preventMoving(e) {
var key = (e.keyCode ? e.keyCode : e.which);
if(key == 13) {
e.preventDefault();
// A
}
}
Then on your HTML:
<textarea onkeyup="preventMoving(event);"></textarea>

The script conflicts with the layout alignment of my responsive template.. How I can fix it?

I copied and customized one script taken from a past stackoverflow discussion. And now I can prevent the "Print screen" with the current key combinations: "ctrl+alt+printscr" , "ctrl+printscr" and "alt+printscr". But after I saved my blogger template, I did some tests with great resoults: the script really works great.. But not as expected, I encountered two problems:
If I click the address bar or leave the page unactive, the script automatically show the background image to cover the layout.
If I try to resize the window, the alignment of the elements fails..
I'm not expert with the javascript language, so probably I made some sort of mistake. I made a picture to show you the nature of the problem.
PS: My layout is totally responsive and I never got problems with the automatic alignment of the elements. Then I tried to remove the latest javascript and again works perfectly. Probably I made some unknown mistake into the code.
I installed many anti-stealing protection scripts, jquery and css (but they not conflict apparently..) Here is a screen demostration of the problem
Here is the code:
<script language='JavaScript'>
function copyToClipboard() {
// Create a "hidden" input
var aux = document.createElement("input");
// Assign it the value of the specified element
aux.setAttribute("value", "You can no longer give printscreen. This is part of the new system security measure.");
// Append it to the body
document.body.appendChild(aux);
// Highlight its content
aux.select();
// Copy the highlighted text
document.execCommand("copy");
// Remove it from the body
document.body.removeChild(aux);
alert("Print screen disabled.");
}
$(window).keyup(function(e){
if(e.keyCode == 44){
copyToClipboard();
}
});
$(window).focus(function() {
$("body").show();
}).blur(function() {
$("body").hide();
});
</script>

Disable link reference tooltip in browsers

I would like to know if there is a way to remove the "tooltip" (I don't know if is the right name for that element) that appears at the bottom-left of the screen when I stop the mouse cursor on a link.
I've taken this screenshot from my google search result page. As you can see the first site is underlined because the cursor is on it (you can't see the cursor because when I take a screenshot it disappears). The tooltip I'm talking about is the one at the bottom-left of the screen, surrounded by the smaller red rectangle.
I couldn't find any information about this element and, honestly, I don't know if it can be removed changing some browser setting.
I'm currently using Firefox and Chrome but I'm looking for a general solution.
Thank you in advance for your help.
you could do it in older browsers, which would hide the status bar.
But to switch it off manually you could put this script in page somewhere...it removes href tag attaches onclick event listener...
$("body").on('mouseover', 'a', function (e) {
var $link = $(this),
href = $link.attr('href') || $link.data("href");
$link.off('click.chrome');
$link.on('click.chrome', function () {
window.location.href = href;
})
.attr('data-href', href) //keeps track of the href value
.css({ cursor: 'pointer' })
.removeAttr('href'); // <- this is what stops Chrome to display status bar
});
You can replace the anchor tag with a button that when clicked executr a javascript function that links to your requested page
window.location.href = /route
Most modern browsers don't allow you to access the status bar to prevent phishing attacks. You can try this javascript but most likely it will be incompatibly with a majority of browsers:
<script language="javascript" type="text/javascript">
function hidestatus(){
window.status='';
return true;
}
if (document.layers)
document.captureEvents(Event.MOUSEOVER | Event.MOUSEOUT | Event.MOUSEDOWN | Event.ONCLICK | Event.MOUSEMOVE | Event.MOUSEUP);
document.onmouseover=hidestatus;
document.onmouseout=hidestatus;
document.onclick =hidestatus;
document.onmousemove=hidestatus;
document.onmouseup=hidestatus;
document.onmousedown =hidestatus
</script>

Make Parent DOM Elements unreadable

I'm making my IFrame to be fullscreen, I want to make DOM Elements in Parent disable [on tabbing] using javascript. Any Idea?
thanks in advance
I think the scenario is very similar to content pop-overs (lightboxes), where you want it to:
Move the keyboard focus to the top of the iframe.
Keep the keyboard focus in the iframe.
To move the keyboard focus into the iframe use a proper link (with href) and trigger:
$("#myIframe").attr("tabindex", "-1").css("outline", "0");
$("#myIframe").focus(); // separate line to ensure the tabindex is applied first.
To keep the focus in the iframe, find the first and last elements and loop them around:
(function(){
var firstLink = $("#myIframe a:first").get(0);
var lastLink = $("#myIframe a:last").get(0);
$(firstLink).keydown(function(e) {
// if you shift-tab on first link, go to last
if(e.shiftKey && e.keyCode == 9) {
e.preventDefault();
$(lastLink).focus();
}
});
$(lastLink).keydown(function(e) {
// if you press tab without shift, loop to first link.
if (!e.shiftKey && e.keyCode == 9) {
e.preventDefault();
$(firstLink).focus();
}
});
})(); // end tabloop anonymous function
JavaScript/jQuery isn't my strong point, so you might need to adjust this. For example, if the first/last focusable element is a form control that wouldn't work.
Also, it is worth knowing that screen readers do not necessarily use tab to progress through the page, they 'arrow' through (browse mode) and do not necessarily trigger focus.
In order to keep them within the iframe you effectively need to 'hide' everything else.
If you have the main content and iframe on the same level this is straightforward, you would start with:
<div class="mainContent">...</div>
<iframe id="myIframe">...</iframe>
When the page loads use:
$("#myIframe").attr("aria-hidden", "true");
When the iframe becomes the focus:
$("#myIframe").attr("aria-hidden", "false");
$("div.mainContent").attr("aria-hidden", "true");
All the techniques for this (in the context of a lightbox) are in a gist here: https://gist.github.com/alastc/7340946
NB: The whole concept of full-screening an iframe sounds a bit dubious, if you provided some context there may be a better solution?

html background ad

I have seen a lot of websites which "wrapper" width is 960px. As a background image they have an image which is clickable (some kind of advertise) and the whole webpage is over that image, like on this site.
Can you give me tutorial or something on that ?
Tom's code was a huge help, but I needed pointer cursor for this type of ad, but not for all the site, so I came up with this solution:
$('body').bind('click', function(e) {
if ($(e.target).closest('#container').size() == 0) {
alert('click');
}
}).bind('mouseover', function(e) {
if ($(e.target).closest('#container').size() == 0) {
$(this).css('cursor','pointer');
} else {
$(this).css('cursor','default');
}
});
In the first place you put the ad image as the website background then basically you have to capture the click on the whole body and check if it was in-or-outside of the page content. To do that you have to check if the event target element have the content wrapper (or wrappers if there are multiple) as one of its parent nodes - if not it means the click was outside of the page content.
If you'd like to do it here on StackOverflow you could do it with this bit of code.
$('body').bind('click', function(e){
if(!$(e.target).closest('#content').length) {
alert('ad outside content clicked');
}
});
Feel free to try it in your javascript console - SO is using jQuery so it will work - when you will click outside of the content area (at the edges of the screen) you will get alert that ad was clicked.
You'd obviously have to replace the alert with any kind of callback you'd have for your commercial - opening a new web page or whatever
Hope that helps
Tom
ps.
Keep in mind that this example is using jQuery for simplicity not native JS so you'd need the library for it to work.

Categories