Accessibility Ionic 4 routing focus is at the bottom of the page - javascript

i have this really annoying problem in my Ionic 4 application that when I change page the screen reader focus is at the bottom of the page which means that my users will have to reverse through the content to get to my main content section.
i did some research and this seems to be a problem with Angular Angular accessibility documentation
Here they suggest that you should set focus on the main content.
However when I attempt this using:
ionViewDidEnter() {
const mainHeader = document.querySelector('#mainContent');
if (mainHeader) {
(mainHeader as HTMLElement)?.focus();
}
}
it doesn't really do anything.
Has anyone had a similar issue and know how I might fix it?
Edit
I have also tried with the viewChild:
#ViewChild('mainPage') mainContent: ElementRef;
ngOnInit() {
this.router.events.pipe(filter(e => e instanceof NavigationEnd)).subscribe(() => {
this.mainContent.nativeElement.focus();
});
}
And setting the mainPage element's tabIndex=0:
<div #mainPage tabindex="0"><ion-router-outlet main></ion-router-outlet></div>
Yet still no focus.

Related

How to scroll a Ionic-Page via JavaScript Test-code

I'm working on an Ionic 6 Webapp based on Angular 13. The client's QA-department want's to perform regression-testing via Selenium test-automatization. For other project's they used window.scrollBy(0, window.innerHeight) to systematically scroll over the page and take screenshots to find regression issues. But this is not possible on Ionic pages, since the HTML body is not scrollable, only the content of the ion-content element. Is there any way to trigger scrolling within the ion-content element via simple JavaScript? I created a Stackblitz where you can see the basic structure of my ionic-page.
So far I tried different things but none worked:
document.getElementsByTagName("ion-content")[0].scrollTo(0, 300);
document.getElementsByTagName("ion-content")[0].scrollToBottom();
document.getElementsByTagName("ion-content")[0].shadowRoot.childNodes[1].scrollTo(0, 300); //tried to access the inner-scroll div
document.getElementsByTagName("ion-content")[0].shadowRoot.childNodes[1].scrollToBottom(); //tried to access the inner-scroll div
why do you want JavaScript to scroll when ionic and angular have a better way to scroll. please check the stackblitz link I have done some code in it.
HTML
<ion-app>
<ion-header>Demo Header</ion-header>
<ion-content>
<div class="large-content-div"></div>
<p id="scrollMe">Scroll to find me</p>
</ion-content>
<ion-footer>Demo footer</ion-footer>
</ion-app>
TS File
export class AppComponent {
name = 'Ionic 6 Angular ' + VERSION.major;
constructor() {
setTimeout(() => {
this.scrollToBottom();
}, 5000);
}
scrollToBottom(): void {
try {
document.querySelector('#scrollMe').scrollIntoView({
behavior: 'smooth',
});
} catch (err) {}
}
}
Here, I have given both the solution if you want to scroll to the bottom scrollToBottom() method or you want to scrollTo some division points scrollTo() method, use it as per the requirement.
This is the best way to call scroll Event in Ionic/Angular

Set text in div tag with expand to top (vk.com tampermonkey script)

During developing of more convenient messages templates for vk.com I faced an issue with message input expanding down instead of expanding up (like it should). Video
Despite some amount of time spent on investigating and resolving the issue - I could not find the reason and a way to fix it. Explaining video
My code to insert text:
function setText(text) {
const chatDiv = document.querySelector("div.im_editable.im-chat-input--text._im_text");
if (chatDiv) {
chatDiv.innerText = text;
}
}
Tag which I insert to:

How to go to particular section without scrolling behavior / Any alternative to scrollIntoView() without scroll behavior?

I am trying to create a page redirection to a particular section, i.e. I want to go to a particular anchor div on a page without scrolling behavior. However, I have a query string in the URL for pagination so the #id method failed for me. I tried "scrollIntoView()" but it contains the page scrolling behavior, which is undesired. May I ask if there is any alternative solution to this problem?
I am using Vue for the frontend & Codeigniter for the backend. Here is my code:
mounted() {
// anchorPageToProductList
if (this.isOnQuery) {
console.log('isOnQuery');
this.scrollToProductList();
} else {
console.log('isNotOnQuery');
}
},
methods: {
scrollToProductList(){
window.addEventListener('DOMContentLoaded', () => {
// scroll animation
document.getElementById('product-list-anchor').scrollIntoView(true);
});
},
Example of my URL case:
http://www.example.com/Product/list?search=&sort=3&type=-1&event%5B%5D=11&pagination=1
Thank you!!
I've tried to unset the scrolling behavior, allow the the page to jump to the desired section, then set back the smooth-scroll behavior. So It now works without the scrolling behavior. Thanks for all the comments:)
My code:
scrollToProductList(){
window.addEventListener('DOMContentLoaded', () => {
// select the whole html & disable smooth-scroll behavior in css
let htmlElement = document.querySelector('html');
htmlElement.style.scrollBehavior = 'auto';
// go to the anchor point
document.getElementById('product-list-anchor').scrollIntoView(true);
// enable smooth-scroll behavior again
htmlElement.style.scrollBehavior = 'smooth';
});
}

React downloads pending images of previous render

(Update: previously, I thought this problem was caused by React Router, but I have stripped React Router out of the code, and the problem still persists. So I have modified this question thoroughly.)
Situation:
I have paginated pages which show a list of images per page. (With "page" I mean the complete content that is visible, I don't mean a separate html file/url.) I would like to nagivate through those pages in an efficient manner.
Problem:
If I navigate quickly enough through the pages, not all images will be loaded in the current page before navigating to the next page. I would expect the browser to cancel all pending unfinished image downloads when navigating to the next page. But this doesn't happen, the browser keeps all the unfinished images pending to be downloaded, until they are all downloaded. Then the images of the page to which I navigated, will be downloaded. This causes a big delay and wasted bandwidth.
Question:
Is it possible to cancel the downloading of "pending" images of the previous page?
Demonstration code:
To test this, use the "network" tab in the developer tool of you browser. Also choose "disable cache" and set the throttle (download speed in developer tool) to something slow like "Slow 3G" otherwise the images will be loaded to fast too see the problem. Then navigate through the pages and see that the list of pending images is stacking up, then click the "disable images" button. Then, no image will be visible on the screen, but the browser still has a large number of pending image downloads open, which is wasted bandwith and causes a delay when new images need to be rendered.
You can test the code here: https://codepen.io/Devabc/pen/PowjqwZ
//This code is using imgur images to demonstrate.
class Page extends React.Component {
state = {
pageNr: 1,
imagesEnabled: true
};
onLinkClick = event => {
const number = event.target.dataset.value;
console.log("Number: " + number);
this.setState({pageNr: number});
};
onButtonClick = event => {
console.log("toggling images");
this.setState(prevState => {
return {imagesEnabled: ! prevState.imagesEnabled};
});
};
render() {
const links = _.range(0, 5).map(number => {
return (
<a href="#" data-value={number} onClick={this.onLinkClick}>
{number}
</a>
);
});
const pageNr = this.state.pageNr;
const imgStart = pageNr * 100;
const imgEnd = imgStart + 100;
const images = this.state.imagesEnabled
? <ImagesPanel imgStart={imgStart} imgEnd={imgEnd} />
: null;
return (
<div>
<h1>Page {this.state.pageNr}</h1>
<div>Page links: {links}</div>
<div><button onClick={this.onButtonClick}>{this.state.imagesEnabled ? "disable images" : "enable images"}</button></div>
<div>Images:</div>
<div>{images}</div>
</div>
);
}
}
function ImagesPanel(props) {
const images = _.range(props.imgStart, props.imgEnd).map(number => {
return (
<Image imageNr={number} />
);
});
return images;
}
function imageUrl(imageNr) {
const hash = imgurHashes[imageNr];
return "https://i.imgur.com/" + hash + ".jpg";
}
function Image(props) {
const url = imageUrl(props.imageNr);
return <img class="myImage" src={url} border='1' />
}
const imgurHashes = ["wmk2tcs","jvqH2X4","r3dz09r","yJYRtvI","33bUPXj","cYsggBH","URAl4lS","xBpS7lq","5LMFxjU","kUrFsMB","GZf8FnO","Er2lmge","22CbMOq","vJcKGb3","U9ALJof","LxfGswQ","YzyyFHI","vin2W11","c0PQRSY","b6b2qva","6UmvLGc","oTtDO7S","LGoOzDl","XD9o83i","dMUi3dj","XpvMqXC","9JYf6o2","8IBe95g","X26sUn3","qb7Taz9","lWd5TCZ","2UKHpPZ","PMmglpV","pZ5ukGv","ymEZize","nYURuNZ","1SToTrZ","GZHTkpe","NH0qm8I","mZRTNyB","FBAoint","nJWbHb9","BI9zvXf","OeT5kWf","JZ1WPQA","6ZK3S2x","z6M8ryn","3yMODr2","bUoicZu","p3ReIJA","dybF5Sh","DH5ZBEH","fMEbpy5","UyMkbSp","EKXikAy","YG3aJm9","4JWIQhV","lgsvM63","A0MepAi","957yfQF","iNkwwNi","aaJpoxO","Vxy5RgX","jZxV1kQ","JuTUNdL","WY4e0cg","xmgTP7A","O35FJpg","VA3fFhv","oGZVPYQ","X9PRsWA","wSYxWzX","xntVddT","rDn0s52","vQPT1rH","GmlqCZt","zntCiSZ","SoEHjHB","bTFF7HW","QjJRzmx","DZxjoKZ","XdIYgsc","aBZChfb","rAIuEHZ","zt9EJD9","vaEJQA1","9c5pjUL","VXW3Ubz","315aFBb","klp7nh9","fsZktFx","x1XmXYX","8HaInVG","6jJOtkE","9aElwYX","R4dDTw7","9hgY0kI","SW8M5sw","R9jcSrP","dlSSb0P","bhxZCLX","mogQ5tz","oHxiJye","PYyOOm3","Ns89wvi","2uWIFDx","nXN5uhB","UMQn1yZ","JCAEJ3a","VTlkiOu","5JyWm3p","RVE8GoO","XVje7aY","C6qr30z","e54jc67","0X4cRbP","qZaU4lT","WUPHAQD","iILnFAb","oCsxMga","hQyN9oX","GQYDzhE","RcTO075","JlTn7jd","0jJHUWZ","iKp7JGx","YaQeN45","Ot5nFpM","8CeXfPJ","m3cQQye","JYXQaj7","pzPglwg","B4a34uo","3dHvLPv","FEn3aTc","coi9Jpy","GU5ih0m","CtnHd4Y","hbOFsRS","xNW9ED4","avCP3XP","mcbapy0","r9E6DqQ","JDjounb","xHGiHZR","LPN7uSK","QwwPGKE","OGTUcVh","OLxQHfA","Mg0QT9y","0ia7Ca3","LKKHYJn","W8nyx6Q","FjLCYY4","5YEYOe9","vLQHljC","jyL5CS9","oCuKxVQ","L2IyiSA","ffkgeN2","FF7bKmo","JpWF0LA","72kPNNw","tmdNh8K","7PBAKy4","EXMlyuO","p5ZkX5b","Iilf92H","fQbVFiU","wj1csk6","rP14xLY","1iQM4nW","XMq6P6Y","dTRijsS","B3Sz2J8","UkSipp2","eAiZQlr","JJkbcAs","sfA0TJ4","foex9pW","IoCvWfI","5yAxTX7","H6EAfeQ","k8d637d","yIQVrZH","bQdJFx5","CiPGw2A","YZLiutP","BQlEKfd","0W032dR","r4PWPJF","mGBYym5","BC7cuX5","TcoRdCZ","KsCfq9T","KaoAUfC","RdDFY7O","HFhBXSb","kOkYQua","mmT60dj","vIqZffx","r7T9b7N","y1Qtgh9","GFwT6Zc","nWUqQQ3","OZMUUjP","x4ocIKv","wy13lyN","fTj8Om8","0AgyD0C","prWbjvJ","MNJdsJk","brS1te1","4xP3P3i","IlcxIrq","bX7vGyi","U4mhrTE","FVMGYVw","zRFW4oG","jpbvgCe","ZJJEUHu","aU5C2XY","jfFsb9b","WfkChky","qNKDvPo","fhgzlkL","4uEHjGz","cRPiOZz","OzA6TSu","iEhVkeo","tXCZZQY","7DBLToo","9rrTZl5","FWO4ugI","kel6MJM","md4dsMN","kvfKaxR","JMyKehy","3jlMpQk","r1DSqFd","ywuONdJ","eCfhJ5y","vDmQ7Sm","BS6VgDy","wwa7mX9","4E1aIuK","D0zqMuZ","ovehv4U","s3yrw4S","GXm3DCm","gugRkdk","6H2WA7e","M6qdVzv","59aJXiY","As2zA50","wyc2LnT","IuhXtFA","l3V5mZS","QoUlc4T","L7XNlhe","cNk7D2j","MPF5iQY","wRjmFCp","7PZSZrU","EpSMAzy","hLaPhDJ","bS3dM0R","SOCUNXb","Q7BIVld","3Yrg85R","cX4KejE","IQbsyz1","i1qMSgy","K5cU3Qb","NeeB0tr","YHZIvUY","I4BmxjZ","BtThGp0","qWPlax5","pDFqfpR","SdET6fw","9EpcwDb","6nDPyRo","B4X4pYN","mkYU9mK","P6A7I0V","Gwb8Wtr","kSkjNsR","vlEb6D8","Gznsyvk","Vpm3QdO","949USnU","8HyKw0G","4tJmhAP","GLXxX89","X4GGU1t","wRVqclU","rEFHH4Q","vLmSGxD","gkFI4kz","kQASxFm","Cq5brtp","KmWYkSo","2IjiGnN","laGj46X","mgYgH2n","I2iwVFj","oFHxy9R","8VWomE0","y1kV08m","PqXAGBv","MNoiTk8","qEEpz2x","e4ipNRw","CfVVnG8","hHnuzRP","YthPF8u","YBigt6A","tQI8AFu","2K0TnUO","GYReGhp","F2XQStI","7j7145H","BVkgYv1","4hfVk8z","faw1ajs","27H4ogL","0a9ZQ2T","041BIAs","dilQYiq","P1e5AXA","5Tia8aA","PFNJRet","t1Nhl5f","UpfH1sp","H2zXF5f","HLdaLwV","30O8VSP","KsaM1XE","jAhqN6P","Yoi2ylE","wkrg47U","ePmLF64","KLSJPrd","aUdvQ0a","VcqZhpH","zEqDYjX","WXLTAbp","shny07g","UqlXlR5","Bpw7KnV","YeP3Zhe","C5NSYDy","Y6GzbOA","6FxTImw","6PXXJmd","FnVsEu0","Ll2zJnC","JvNbUcB","nEOXggf","t28tvPa","m2qG0pl","KjkkYG5","kCcUVrD","g2G823J","ZyD1f4Z","P3SfyR5","QsA5vHF","DpSxArz","dKt3T4S","w0GfNCC","kvyacI9","Gqdr7qd","KlHHMzZ","VVZ7HVw","hFG5mwe","D4S9tPV","SMVSpk3","MYzi6pi","BzQeber","v7E7VCw","a03HdC6","KSAiRwR","7WkzcMx","z8iVQZP","qCDNLpI","TUerfpq","X08dKd5","qkmDrys","k0MjIxD","vIgbAlY","15TeGJg","j7vx4tU","DNvqPee","2jEQHaF","5Q1M9HL","EFninXx","0VttnZV","XKgdZGL","fDSOKh8","1i5Fhk1","wmUU0tL","O7SVAVF","WMoTmty","UUVTKaF","n9EPI8R","QqnEITA","lao3U57","ITabVKK","ph97wz5","bFfdCGt","9KWJKs3","aKKQd6o","jxzaSfj","6Gv8gBL","iDS0A1T","bTAS7ej","5FvLcFu","GtIFmNQ","kJAU7gn","UZCPmpz","68yuNFU","TuO5PNi","lMV0piH","taKCu3Y","PmNa3M4","Z6gErEZ","5qrVmOD","N5yxt2l","LhrvwLr","QhBn6p8","2kpPEe8","dnxlCK3","GMDnQ32","3qH62l5","Jy6aHR2","2tIZHcB","w0zzrUJ","aSh1mwr","fwCCSBI","k5osQCu","byHHnMX","Uu9Dq9I","K9sC2OO","CsFf1Kz","G28GqCu","OOPc79G","be5NrVR","C5XAmr2","rDNSwSj","AI4BrBq","hGwueuh","EpG6zfG","QORwSKm","sWDMpiD","U2QfeTp","kUqUudt","PGMbcrN","bEaFtGN","KPrPXxO","4iFaBhm","fDcFhWG","P1M2Ld5","aPAlbHH","8ye6kdq","ztVBpFQ","SVL8ujT","5CwT2Og","nIqakeV","SM3Jcoz","QdAk2M4","zpLnMrH","fglMOex","ynj6fe7","YG1DOZh","aJ50pkC","SbvVCaf","azLfxiY","gdw8DHE","1U00sfi","p3zgLIS","h9cTNnw","Z1tt3RC","HHnBLCI","hmkeUl1","aMKRR0h","6MteQjh","PMZzXiM","v2uh5Mk","QEDz82m","70LhmSw","KEGMEbg","tlG769G","gyoNASp","AUnDdta","z1TZP9m","nVmmkCH","IIDRcHT","8m1Go3S","LsscGjy"];
ReactDOM.render(
<Page />,
document.getElementById('root')
);
The screenshot below shows the list of pending images in the browser, even though no images should be rendered at this moment, because the React component that should render the images is already unmounted/removed.
The problem is not caused by React. I'm not sure who to blame for this problem, it could be the Document Object Model standard, HTML or the browsers implementing it. The problem is that once an image is attached to the DOM (when the browser starts to render a HTML page), the browsers will start to download it, even when the image is later removed from the DOM.
There are however a number of solutions to solve this problem.
Solution 1: src attribute modification to cancel downloads
The src attribute is mandatory in the img element of HTML, but setting it to an empty string (clearing), results in cancellation of the download of the image. (This may not work on all browsers.)
This solution is described here: How to cancel an image from loading and demonstrated here https://jsbin.com/zakeqaseru/1/edit?output and here https://jsfiddle.net/nw34gLgt/
(Use the browser developer tools to monitor whether the image download is cancelled.)
The src can be changed to the empty string using setAttribute(). Setting it to null has the same effect as setting it to the string "null", which would also work, but is less clean.
Removing the src attribute doesn't work on most browsers.
Whether this solution works may differ per browser. Older browsers may cause the web page itself to be requested when src is removed, as mentioned here: https://gtmetrix.com/avoid-empty-src-or-href.html
This could be a very big performance hit on both the brower and the web server.
In case the browser cannot deal with this, a similar solution would be to use the background-image property on a HTML element like a div instead of an img.
This src-clearing solution is implemented in React-Image:
https://github.com/mbrevda/react-image/pull/223
this.i.src = ''
try {
delete this.i.src
} catch (e) {
// On Safari in Strict mode this will throw an exception,
// - https://github.com/mbrevda/react-image/issues/187
// We don't need to do anything about it.
}
delete this.i
A solution in React would be something like this:
class Img extends React.Component {
constructor(props) {
super(props);
this.imgRef = React.createRef();
}
componentWillUnmount() {
try {
this.imgRef.current.src = '';
delete this.imgRef.current.src;
} catch (e) {}
}
render() {
return (
<img {...this.props} ref={this.imgRef} />
);
}
}
Then you can use it like this:
<Img src="...some image url...." />
Solution 2: lazy loading src
By setting the src attribute under a programmatic condition, you can make sure that images are only loaded when needed. This solution can also be combined with other solutions to make sure that images that are downloaded, are cancelled when the associated React elements are unmounted.
The data-src (custom data) attribute is often used in this approach, to copy its value to src when an image needs to be rendered.
This solution may still download unmounted images.
Solution 3: Chrome's native lazy-loading
This solution exists since Chrome 76 (July 2019?). Chrome has a loading attribute for images and iframes, which can be set to "lazy" to defer downloading of the resource until it reaches a calculated distance from the viewport.
The loading attribute isn't supported by many other browser.
For more info, see:
https://web.dev/native-lazy-loading/
https://github.com/scott-little/lazyload/
Lazy-loading request for Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=1542784
This solution may still download unmounted images.
Additional information sources
https://developers.google.com/web/fundamentals/performance/lazy-loading-guidance/images-and-video
https://imagekit.io/blog/lazy-loading-images-complete-guide/

IntersectionObserver blur element on unfocus

Recently I have been working on a project with an image grid for a friend of mine. The idea behind the website was that when the images in one row come into focus it pops out and the remaining go back into their original sizes.
After some research I found the IntersectionObserver API for JavaScript and the following example: http://codepen.io/pawelgrzybek/pen/YWqWXJ I modified the code to change the threshold to
let options = {
threshold: [1.0]
};
but those are just personal modifications, the issue I had was when I scroll down to the next element that comes into focus using the visible class but the previous elements still stay in focus. I wasn't sure about the best way to go about doing this.
I found examples and was able to notice the following code:
function updateStatus(visiblity) {
console.log(visiblity);
const status = document.querySelector('.status');
status.textContent = visiblity;
status.className = 'status status--' + visiblity;
}
but this is only for the top bar in the Simple Example I needed some help figuring this out, please and thank you everyone!

Categories