How to catch dojox.mobile.view events? - mobile

I am developing a mobile web app using dojo. I have a view with a video in it.
<div id="v1" dojoType="dojox.mobile.View">
<h1 id="h1" dojoType="dojox.mobile.Heading" back="Media" moveTo="media">IT Models</h1>
<video id="vid1" controls width="100%" poster="itModels.jpg"><source src="itModels.m4v"></video>
</div>
When I start the video on the view and then click the back button to go to another view, media, the video keeps playing. I want to catch the event when the v1 view is no longer visible so I can turn off the video with a dojo.byId("vid1").pause();
My issue is that I can't catch any of the events associated with the V1 view
There is documented a number of events associated with dojox.mobile.view at http://dojotoolkit.org/api/1.6/dojox/mobile/View but I can't catch any. I have tried with both the dojo.connect and the new 1.7 dojo.on function.
dojo.on(dojo.byId("v1"),"onAfterTransitionOut",function() {dojo.byId("vid1").pause()});
The onAfterTransitionOut event never gets fired.
Any ideas?

I ended up punting and doing something like this to make it work:
dojo.subscribe("/dojox/mobile/afterTransitionOut","v1",function(x) {if (-1 != x.toString().indexOf("v1"))dojo.byId("vid1").pause();});

Related

Snackbars not read by screen reader when triggered from a dialog

Issue:
We are triggering a snackbar from a button within a dialog and the snackbar is not being read. It reads fine when triggered from a button outside the dialog.
Desired behavior:
When the user clicks the "OPEN SNACKBAR" button from within the dialog the snackbar is read by the screen reader.
Codesandbox:
https://codesandbox.io/s/dialog-snackbar-testing-sje9p?file=/demo.js
Steps to reproduce issue:
Note: If you would like to see how the snackbar should be read by the screen reader Click the first "OPEN SNACKBAR" button presented.
Open NVDA screen reader or JAWS screen reader
Go to https://codesandbox.io/s/dialog-snackbar-testing-sje9p?file=/demo.js
Click "OPEN DIALOG" button
Click "OPEN SNACKBAR" button
Testing details:
Button trigger: JAWS + Chrome = PASS
Button trigger: JAWS + Firefox = PASS
Button trigger: JAWS + Edge = PASS
Button trigger: NVDA + Chrome = PASS
Button trigger: NVDA + Firefox = PASS
Button trigger: NVDA + Edge = PASS
Dialog button trigger: JAWS + Chrome = FAIL
Dialog button trigger: JAWS + Firefox = FAIL
Dialog button trigger: JAWS + Edge = FAIL
Dialog button trigger: NVDA + Chrome = FAIL
Dialog button trigger: NVDA + Firefox = FAIL
Dialog button trigger: NVDA + Edge = FAIL
I think you are getting lucky that your first example works. It looks like you are not using aria-live correctly in either test case. aria-live is meant to exist on the page and not be added dynamically itself.
That is, for aria-live to work correctly all the time no matter which browser or screen reader or platform, your page should look something like this:
Before
<div>stuff</div>
<div aria-live="polite"> <!-- intentionally empty --> </div>
<div>stuff</div>
And when you add the snackbar message, it would be injected into the empty <div> and would be announced properly.
After
<div>stuff</div>
<div aria-live="polite"> snackbar stuff </div>
<div>stuff</div>
What doesn't work reliably is adding aria-live elements dynamically. The following may or may not work on some browser + screen reader combinations:
Before
<div>stuff</div>
<div>stuff</div>
After
<div>stuff</div>
<div aria-live="polite"> snackbar stuff </div>
<div>stuff</div>
Note that my examples use aria-live directly. In your sample, your snackbar is using role="alert" which gives you an implicit aria-live="assertive". For the purposes of having the text announced, it doesn't matter if you have "polite" or "assertive". The main issue is adding a role="alert" (ie, aria-live) dynamically. It generally doesn't work.
The fact that you hear it with the button example but not the dialog example is weird but the first thing you have to eliminate is the dynamic aria-live. If you do that and it still doesn't work in both cases, I can take another look.
The issue is multiple live regions on the page.
When you add your first snackbar you expose role="alert" (which is the same as aria-live="assertive" effectively).
This should work pretty well across all browsers (a single alert).
However, multiple aria-live regions tend to fail in numerous screen readers, especially when dynamically added and removed.
The best way to deal with this is to move all screen reader alerts into a single aria-live region and set up a message queue to handle alerts at the application level, this way you only ever have one aria-live region (2 if you need a polite and an assertive option) and won't run into as many issues.
A quick Example / the concept
HTML
<!-- You can add and remove these at will, this is the dynamically added snackbar -->
<div class="snack"></div>
<!-- This will **always** exist on the page and we will update the contents from our message queue -->
<div id="alertAnnouncer" aria-live="assertive">
</div>
Pseudo code
// Adds the snack bar with the specified text. This has nothing to do with the screen reader message queue, it is just to represent your current function to add a snack bar.
addSnackBar("Alert Text");
// this is how we add messages to the queue.
addScreenReaderAlert("Alert Text");
// we hold our messages here for processing.
var messageQueue = [];
function addScreenReaderAlert(textToAnnounce){
messageQueue.push(textToAnnounce);
//CODE TO ADD: start the interval to process the queue if it is not running, if no update has happened in the last x seconds process the message immediately and set the last time processed or something similar.
}
function processMessageQueue(){
if(messageQueue.length > 0){
//CODE TO ADD: get oldest message (you would use slice etc. and remove it entirely from the queue)
// ultimately we want this
let oldestMessage = messageQueue[0];
// we change the inner HTML of the aria-live region so the oldest message is announced.
return document.querySelector('#alertAnnouncer').innerHTML = oldestMessage;
}else{
//CODE TO ADD: clear the interval
}
//NOTE: to save CPU cycles I would stop the interval once the message queue reaches 0 length and then restart the message queue interval when you fire the addScreenReaderAlert() function.
}
// this is purely to show there is an interval on the queue, you would set and unset this as described above.
Window.setInterval(processMessageQueue, 1000);
// NOTE: The interval delay should be set to a value large enough to allow a screen reader to announce the messages. So for a simple snackbar this can be shorter, but if you want to announce longer messages you need to set this higher. Note that the interval is purely for spreading out multiple messages added in quick succession so you don't overwrite them too quickly.
Explanation of code
That all looks complicated and messy, my apologies!
But essentially we want a way of:
adding a message to a queue
processing that queue with a gap between each update to the aria-live region.
Ideally we process items immediately if there hasn't been a message in the last x seconds.
The gap between messages is to ensure you do not overwrite the message currently being spoken.
You can set up aria-live regions so that you can continue adding messages to them (appending the message rather than replacing), but this is not always stable in all screen readers (older NVDA, ORCA etc. don't always read new items after a certain length). This workaround is pretty robust once you fine tune the message timings etc.
The message queue, although a little bit of work to set up in the first place, is also a super easy way to manage all alerts etc. across your whole application without worrying about multiple alerts overlapping etc.
If you need more explanation (as I am not sure if I "waffled" on in this explanation 🤣) just let me know.

Leaflet map not properly displayed in ionic react app [duplicate]

I am using tabs to display clear content, but one of them stopped downloading well since it is in the data-toggle tab. It is a Leaflet map.
Here is the code :
Navbar code :
<ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#home">Données principales</a></li>
<li><a data-toggle="tab" href="#carte">Carte</a></li>
</ul>
<div class="tab-content">
<div id="home" class="tab-pane fade in active">Lorem ipsum</div>
<div id="carte" class="tab-pane fade"> **//see script below\\** </div>
</div>
Script :
<div id="carteBenef"></div>
<script type="text/javascript">
$(document).ready(function () {
var map = new L.Map('carteBenef');
var cloudmadeUrl = 'http://{s}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png',
subDomains = ['otile1', 'otile2', 'otile3', 'otile4'],
cloudmadeAttrib = 'Data, imagery and map information provided by MapQuest, OpenStreetMap and contributors, CC-BY-SA';
var cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18, attribution: cloudmadeAttrib, subdomains: subDomains});
var iades = new L.LatLng(<?php echo $beneficiaire->latitude . ', ' . $beneficiaire->longitude; ?>)
map.addLayer(cloudmade).setView(iades, 15);
var benefLocation = new L.LatLng(<?php echo $beneficiaire->latitude . ', ' . $beneficiaire->longitude; ?>);
var benef = new L.Marker(benefLocation);
map.addLayer(benef);
benef.bindPopup("<?php echo htmlspecialchars($beneficiaire->nom) . ' ' . htmlspecialchars($beneficiaire->prenom); ?>").openPopup();
});
</script>
The map was appearing well before I put it in the tab, does someone have an idea why it does not work now? Thank you =)
Welcome to SO!
If your Leaflet map suddenly works correctly after you resize your browser window, then you experience the classic "map container size not valid at map initialization": in order to work correctly, Leaflet reads the map container size when you initialize the map (L.map("mapContainerId")).
If your application hides that container (typically through CSS display: none;, or some framework tab / modal / whatever…) or later changes its dimensions, Leaflet will not be aware of the new size. Hence it will not render correctly. Typically, it downloads tiles only for the fraction of the container it thinks is shown. This can be a single tile in the top left corner in the case of a container that was entirely hidden at map initialization time.
This mistake often arises when embedding the map container in a "tab" or "modal" panel, possibly using popular frameworks (Bootstrap, Angular, Ionic, etc.).
Leaflet listens to browser window resize event, and reads again the container size when it happens. This explains why the map suddenly works on window resizing.
You can also manually trigger this update by calling map.invalidateSize() when the tab panel is displayed (e.g. add a listener on the tab button click), at least the first time the container is rendered with its correct dimensions.
As for implementing the tab button click listener, perform a new search on SO on that topic: you should have plenty resources available for that matter, for most of the popular frameworks.
First, thank you #ghybs for your good explanation on why the Leaflet maps are not shown properly in these cases.
For those who tried unsuccessfully the #ghybs's answer, you should try to resize your browser instead of calling a method of the map object :
window.dispatchEvent(new Event('resize'));
As #MarsAndBack said, this fix may cause issues on pan/animation/etc features that Leaflet's invalidateSize provides.
I have this problem because i used modal bootstarp. and it not solved anyway. i tried map.invalidateSize() and window.dispatchEvent(new Event('resize')); but not fixed.
finaly it fixed by this:
$('#map-modal').on('shown.bs.modal', function(event) {});
'shown.bs.modal' event means when modal is completely load and not any confuse on size, now inside that write your codes.
For those like me weren't able to solve the challenge with the precious #ghybs explanation (map.invalidateSize()) and were able to do it with #gangai-johann one, there's still a chance you may do it the right way. Put your invalidate instruction inside a setTimeout, as it may be too early to dispatch that instruction.
setTimeout(() => {
this.$refs.mymap.mapObject.invalidateSize()
}, 100)
Refer to https://stackoverflow.com/a/56364130/4537054 answer for detailed instructions.

Aframe cursor fuse working, when it not expected

strange behavior with fuse cursor. I've set fuse false for cursor both mainscene and a-cursor, but fuse still appears on hover.
This problems is apeearing when testing on ios, android. From desktop browser working fine.
<a-camera id="default_angle" camera position="0 0 0" look-controls wasd-controls>
<a-cursor>
<!-- by default click should be not expected on hover object (fuse) -->
</a-cursor>
</a-camera>
...
<a-box id="motor" color="red" position="0 0 -5"></a-box>
<!-- on hover to this object from mobile fuse should not appear -->
...
var motor = document.querySelector('#motor');
motor.addEventListener('click', function (evt) {
alert('clicked');
});
Please checkout this codepen
https://codepen.io/sevenspring/pen/XWWzNvK
Here may be thought, that clicks produced by touch (tap on mobile screen) while cursor is hovered on object.
but you may check this example with gyro.
https://parent.glitch.me
in vr mode we can hover cursor on button "play/pause" by gyroscop moving, it still will produce clicks.
https://glitch.com/edit/#!/join/d1afa869-dea2-47ec-9904-e851e451d832
Answer from Dan S. is correct.
When we use without attribute "cursor" , fusing event is not fired, as it is expected by default
There are actually a number of issues here.
One issue is that the CodePen demo is using <a-cursor fuse="false" cursor></a-cursor>. This is essentially two cursors as you're adding a second one as a component via cursor that hasn't been configured.
Another issue is that cursor fuses by default on mobile if not explicitly set to cursor="fuse: false;"
Another issue is that you're listening for the click event, which is capturing the click when you mouseup after hovering over the entity.
I believe fuse was designed to allow for click without clicking, but while also allowing users to click if they have the ability.
If you want to test when something is fusing exclusively, you may want to try listening for the fusing event rather than click.
<a-cursor fuse="false"><a-cursor>
Or
<a-entity cursor="fuse: false;">
<!-- you will need to provide your own cursor geometry -->
<a-entity>
Then listen for fusing:
this.el.addEventListener('fusing', function (e) {
console.log(e);
});
I hope this helps.

How to click on a deeply buried button in div classes via protractor. No id

I am trying to click a button that is buried in div classes in the code via protractor.
I am pioneering a protractor project for my work and have reached a point where I no longer know what to do. I have a button that is buried in div classes and is not allowing me to click. I have tried using mouseMove to get over to the coordinates of the button, I have tried using the className of the specific button, etc. The button does not have an id. The id is not the issue as I have tried clicking a different button, equally buried in divs, by it's id. I need to know how to get through the layers of divs in order to click the button because the rest of the tests will be dependent on it.
APPLICATION CODE:
::before
<dashboard-label>
<div class="att-topic-analysis-tabs">
<div class="att-button-group">
<button class="btn btn-default btn-lg att-close-topic ng-scope"
role="presentation" tabindex="-1"
ng-click="removeTopic(currentTopic.id)" translate>
Close Topic
</button>
</div>
</div>
PROTRACTOR TEST:
it('Closes Topic Successfully', function(){
//opens the first available topic
openTopic.click();
//checks that the URL contains 'topics' after 5 seconds
browser.wait(proExpect.urlContains('topics'), 5000);
var closeTopic = element(by.className('att-close-topic'));
//browser.wait(proExpect.elementToBeClickable(closeTopicButton), 5000);
console.log(closeTopic);
closeTopic.click();
browser.wait(proExpect.urlContains('home'), 5000);
});
As you can see, the Close Topic button is kind of buried in div classes and the standard click isn't working. Any info would be greatly appreciated
If the closeTopic locator is finding the element, but failing to click it, check to make sure there's only one matching element in the DOM, and that it's visible. My favorite way to check the DOM is just ctrl-F in Chrome inspector and paste the exact CSS that the test is using (.att-close-topic). And to check that what it's getting is visible, use
console.log(closeTopic.isDisplayed());
This can be a big gotcha in protractor, because it doesn't fail (only warns) when there are multiple matches on the page, and it defaults to the first match rather than the first visible match, which drives me nuts, because it's very rare that you want to do anything with a non-visible element on the page.
This will be partly opinion, but just to add a layer to the conversation...
Sometimes the solution to locating a troublesome element on the page is to go back to the developers and make the page more testable. I've seen testers spend hours or days crafting brilliant workarounds to access a stubborn element, and the end result was a fragile, complicated end-to-end test (and aren't they fragile enough already?).
Sometimes a 5-minute conversation with a developer can result in a quick change in the production code (e.g. add a unique ID) that avoids all that effort and yields a much better result, more stable, more simple. But this requires open conversation between the dev and test team, and a culture that values testing as a primary activity enough to make those testability changes to production code that is otherwise working just fine.
This is what you want to read to help you debug why your test doesn't work.
Also, you might want to start adopting await/async since the control flow will go away in the future.
http://www.protractortest.org/#/debugging
try this
var closebutton=element(by.css("[ng-click="removeTopic(currentTopic.id)"]"),
EC = protractor.ExpectedConditions;
Waits for the element to be clickable.checks for display and enable state of button
browser.wait(EC.elementToBeClickable(closebutton), 10000);
now use : closebutton.click();

on-hold also firing ng-click on Samsung phones

I have two functions openMenu() and openItem()
* If i click on item it will activate ng-click with openItem()
* If i hold, it will trigger on-hold with openMenu() calling $ionicActionSheet.show({.....});
It works fine on Motorola and Asus devices, but Samsung is firing twice.
Is it opening the menu and opening the item (causing the change of the view and closing menu at once)
Any solutions?
PS:
I'm using Ionic Framework
I have discovered something.. If you hold and stay with your finger in the same point before move up yout finger, it will occurs, BUT.. if you do a litte swipe (move to any side, just 1mm movement) it will not!
I had the same problem and found the solution.i code as bellow.
<div ng-repeat="array in location.location track by $id($index)">
<div on-hold="array.qty = 0;array.showInput=true" on-release="array.showInput=false">
<label ng-show="!showDisabled(array.showInput)" ng-bind="array.qty" ng-click="$event.preventDefault();$event.stopPropagation();array.qty = array.qty + 1;></label>
<label ng-show="showDisabled(array.showInput)" ng-bind="array.qty"></label>
</div>
</div>
if i directly write ng-show="array.showIput" AND ng-show="!array.showIput" then ng-click fire after on-hold.
So i took two label one show after on-hold, without click-able label the other label with click-able and show it using the function. Then function is
$scope.showDisabled = function(val){
setTimeout(function(){
if(val){return true;}
else{return false;}
});
}

Resources