I am trying to figure out how to click a button on the D365 ribbon. The button will refresh the page and i am going this route because ultimately i want to refresh all of the elements on the page.
I have tried accessing via query selector with no luck
document.querySelector("#rr_jobprofile\\|NoRelationship\\|Form\\|Mscrm\\.Modern\\.refreshCommand72 > button").click();
html for the button
<button aria-label="Refresh" aria-hidden="true" title="Refresh" tabindex="-1" data-id="rr_jobprofile|NoRelationship|Form|Mscrm.Form.rr_jobprofile.RefreshModernButton" data-lp-id="Form:rr_jobprofile-rr_jobprofile|NoRelationship|Form|Mscrm.Form.rr_jobprofile.RefreshModernButton" type="button" class="pa-ak pa-kx pa-go pa-ep pa-aj pa-om pa-at pa-sx pa-sy flexbox"><span class="pa-az pa-ah pa-a pa-hh "><span class="pa-ho pa-hj pa-st pa-cd pa-bd pa-a pa-at "><img src="/uclient/resources/images/Refresh.svg?v=1.4.2043-2012.2" alt="" title="" class="pa-oh pa-cg pa-bd pa-cc "></span><span aria-label="Refresh" tabindex="-1" class="pa-hj pa-bd pa-st pa-v pa-e pa-cm pa-oz pa-cl ">Refresh</span></span></button>
Really really really recommend against manipulating or navigating the DOM. There are methods in the formContext.Controls collection to refresh any control on the page that requires it, or the page itself. Refreshing an HTML web resource is a little less obvious, but I've had good success using the getSrc() and setSrc() functions of the control. This function (not mine I got it on some blog years ago and added it to my toolbox) works very well, and will work both on the form (e.g. on load or on change) and from the ribbon.
function refreshWebResource(executionContext, WebRrscName) {
var _crmForm = executionContext.getFormContext();
var webResource = _crmForm.getControl(WebRrscName);
if (webResource != null) {
var webResourceSrc = webResource.getSrc();
webResource.setSrc(null);
webResource.setSrc(webResourceSrc);
}
}
Code's original source: https://community.dynamics.com/crm/f/microsoft-dynamics-crm-forum/232589/refreshing-the-web-resource-in-dynamics-365
Try
document.querySelector('[data-id="rr_jobprofile|NoRelationship|Form|Mscrm.Form.rr_jobprofile.RefreshModernButton"]').click()
Selectors for data attributes use attribute selectors syntax instead of starting with a tag name or characters # or .. For example
button[data-id=xxxx] selects a button whose data-id attribute value is exactly xxxx.
button[data-id|=xxxx] selects a button whose data-id attribute value starts with xxxx`.
`
Related
I have created a mdl-badge with data-badge class to add a alert icon on my page. How can I dynamically change the data-badge data (i.e. rather than hard coded 5) in my controller so the displayed number corresponds to number of alerts reported by backend server. Doing {{alertcount}} does not seem to work. The html is:
<a class="mdl-badge mdl-badge--overlap mdl-badge--no-background" data-badge="5" href="#monitor"><i id="ttalert" class="material-icons">messages</i></a>
<div class="mdl-tooltip" for="ttalert">Alerts</div>
OK, so this is what I have done and it seems to work:
alerticon = document.getElementById('alertbadge');
alerticon.setAttribute('data-badge', alertcount);
currently I'm trying to make it so that when the user clicks a link it submits the corresponding form via javascript. I used document.getElementById("id").submit() as a basis on how to send the form so my code should act similar to it in my understanding.
Here's the code:
function run(clickedLink){
clickedLink.id.submit(); //I did it like this since document.getElementById just gets the form id and since link and form have similar id's I thought it would send
}
<form id = 'formId'>
<a href = '#' id = 'formId' onclick = run(this)>Link</a>
</form>
I tried going with name = 'formId' too but it still doesn't run as I wanted it too.
Note: doing this since this code iterates dynamically and the id gets updated i.e. formID1, formID2...
Better ways to implement this are welcome too
Modify your function as follows
function run(clickedLink){
clickedLink.parentNode.submit(); // parentNode refers to the form element
}
You cannot use same id on the same page for more than one element. This is against HTML and DOM specifications https://softwareengineering.stackexchange.com/questions/127178/two-html-elements-with-same-id-attribute-how-bad-is-it-really .
You can change it to class if you want to reuse or you can change the id itself of other element. Also links are not recommended to submit the form. Their job is to navigate
Try this:
<a href="#" onclick="document.forms[0].v.value='Link1';
document.forms[0].submit();">Link 1</a>
One Basic thing:
-ID's are used to Uniquely Describe The Element in DOM Hierarchy. Please Don't Repeat it. (it is really bad)
Now to the answer:
function run(x){
var y=findParentForm(x); /* if Id's aren't Unique */
// If iD's are Unique :- var y=document.getElementById("formId");
y.submit();
}
function findParentForm(elem){
/*
This function will find exact parent form
even if link or elem is inside <div> or other complex DOM structure
This will Only return the parent <form> of that elemnt
*/
var parent = elem.parentNode;
if(parent && parent.tagName != 'FORM'){
parent = findParentForm(parent);
}
return parent;
}
<form id='formId' action="Server Side Script" method="GET/POST">
Link <!-- change id of link -->
</form>
I am using the youtube loadingbar tool found at www.onextrapixel.com, and it's pretty straightforward with regards to everything.
However, I have multiple <a> tags that have different href attributes and will load the content area (target) with different content. The problem is when I initialize the loadingbar function to a class, say:
$('ajax-call').loadingbar(options);
and I have multiple a tags with the same class say:
<a id ="link1" class = "ajax-call" href="hello1.html" data-type="POST" data-target="#Content_Area">Link 1</a>
<a id ="link2" class = "ajax-call" href="hello2.html" data-type="POST" data-target="#Content_Area">Link 2</a>
<a id ="link3" class = "ajax-call" href="hello3.html" data-type="POST" data-target="#Content_Area">Link 3</a>
<a id ="link4" class = "ajax-call" href="hello4.html" data-type="POST" data-target="#Content_Area">Link 4</a>
In this example I have 4 links, so the ajax call, along with success and done functions, is repeated 4 times. Each time it loads the same (correct) content and executes the same functions (correct functions) for each respective link, but it does that 4 times, which results in completely messed up everything, since I am initializing other objects (which get initialized 4 times).
All the content looks correct but nothing in the content area works (modals, etc.).
If I make only one link in the side menu, all works fine. If I initialize each link separately by ID, all works fine, but if I group them all in one class, this happens. How do I solve this?
classes starts with a period
$('.ajax-call').loadingbar(options);
And it looks like the plugin is poorly written, it does not return this.each(function().. it simply does
$.fn.loadingbar = function(options){
el = $(this),
href = el.attr("href"),
target = el.data("target"),
...etc
so calling it on a collection of elements probably doesn't work properly, try
$('.ajax-call').each(function() {
$(this).loadingbar(options);
});
I've got the following HTML code, which essentially pertains to a post where I announce something in just a few lines, end it with "[...]", and add a "Read more" link-button at the bottom. When this button is clicked, additional content that's hidden will fadeIn as the button disappears, leaving visible the introductory text and the one that was hidden -- simple enough. Now, I've already written the code for this, but the complication comes when I try to also remove that "[...]" (from the post where the click button happened) that I included in the sneak peek. Here's the HTML:
<div class="entry">
<p>Welcome. Talk about something briefly and click below for more. [...]</p>
<div class="slide-content">
<p>Hidden content.</p>
</div>
<span id="revealer" class="button">Read more</span>
</div>
Classes "entry" and "button" belong to my CSS file, while "slide-content" belongs to my .js file to control the fadeIn effect. The ID "revealer" also belongs to the .js file for the same purpose. This HTML is wrapped in a div tag with a class of "box". This is the format that each post follows, exactly the same format with the same HTML elements -- every time an announcement needs to be made, it's just a matter of putting the content between the paragraph tags and publish. Here is where my problem comes in, since I can't find a way to remove the "[...]" only in the post where the button has been clicked. I tried doing the following but it resulted in the deletion of all "[...]" throughout multiple posts:
$('.entry p').each(function() {
var textReplace = $(this).text();
$(this).text(textReplace.replace('[...]', ''));
});
Summary:
I need to remove the "[...]" text only from the post where the user has clicked on (the "Read more" button). The idea is to have this removed while at the same time the hidden content fades in.
I've been able to accomplish the above but for all instances of "[...]". I need to sophisticate my selection by modifying my jQuery code or the HTML.
Option 3 is to get rid of this "[...]", but I would like to leave it there to let the user know she has more content to read, and I would like to have that "Read more" button in all posts for consistency.
~Thanks in advance!
First, you mention you have multiple of these. In that case, this:
<span id="revealer" class="button">Read more</span>
will not work. id attribute has to be unique per document, i.e. you can have at most one element with the specific id value.
If you make your HTML (for each of the blocks) like this:
<div class="entry">
<p>Welcome. Talk about something briefly and click below for more. [...]</p>
<div class="slide-content">
<p>Hidden content.</p>
</div>
<span class="revealer button">Read more</span>
</div>
and your JS like this:
function replace(fromp) {
var textReplace = fromp.text();
fromp.text(textReplace.replace('[...]', ''));
}
$('.revealer').click(function() {
var fromp = $(this).siblings().eq(0);
replace(fromp);
});
it will work properly. Working example:
http://jsfiddle.net/G4t7Q/
Hope this helps.
When you run your page initialization script, you could use jquery to select all of the posts and all of the remove buttons and link them up via their click event. I've created a JSFiddle example, but here's the jist of it:
var removers = $(".remover")
var posts = $(".post")
for (var i = 0; i < removers.length; i++) {
$(removers[i]).click( { post: posts[i] },
function(event) {
var textReplace = $(event.data.post).text()
$(event.data.post).text(textReplace.replace('[...]', ''))
}
)
}
This is a simplified example; it assumes the posts and buttons are sorted in the markup.
When a web page is loaded, screen readers (like the one that comes with OS X, or JAWS on Windows) will read the content of the whole page. But say your page is dynamic, and as users performing an action, new content gets added to the page. For the sake of simplicity, say you display a message somewhere in a <span>. How can you get the screen reader to read that new message?
The WAI-ARIA specification defines several ways by which screen readers can "watch" a DOM element. The best supported method is the aria-live attribute. It has modes off, polite,assertive and rude. The higher the level of assertiveness, the more likely it is to interrupt what is currently being spoken by the screen reader.
The following has been tested with NVDA under Firefox 3 and Firefox 4.0b9:
<!DOCTYPE html>
<html>
<head>
<script src="js/jquery-1.4.2.min.js"></script>
</head>
<body>
<button onclick="$('#statusbar').html(new Date().toString())">Update</button>
<div id="statusbar" aria-live="assertive"></div>
</body>
The same thing can be accomplished with WAI-ARIA roles role="status" and role="alert". I have had reports of incompatibility, but have not been able to reproduce them.
<div id="statusbar" role="status">...</div>
Here is an adapted real world example -- this up-level markup has already been converted from an unordered list with links into a select menu via JS. The real code is a lot more complex and obviously could not be included in its entirety, so remember this will have to be rethought for production use. For the select menu to be made keyboard accessible, we registered the keypress & onchange events and fired the AJAX call when users tabbed off of the list (beware of browser differences in timing of the onchange event). This was a serious PITA to make accessible, but it IS possible.
// HTML
<!-- select element with content URL -->
<label for="select_element">State</label>
<select id="select_element">
<option value="#URL_TO_CONTENT_PAGE#" rel="alabama">Alabama</option>
</select>
<p id="loading_element">Content Loading</p>
<!-- AJAX content loads into this container -->
<div id="results_container"></div>
// JAVASCRIPT (abstracted from a Prototype class, DO NOT use as-is)
var selectMenu = $('select_element');
var loadingElement = $('loading_element');
var resultsContainer = $('results_container');
// listen for keypress event (omitted other listeners and support test logic)
this.selectMenu.addEventListener('keypress', this.__keyPressDetector, false);
/* event callbacks */
// Keypress listener
__keyPressDetector:function(e){
// if we are arrowing through the select, enable the loading element
if(e.keyCode === 40 || e.keyCode === 38){
if(e.target.id === 'select_element'){
this.loadingElement.setAttribute('tabIndex','0');
}
}
// if we tab off of the select, send focus to the loading element
// while it is fetching data
else if(e.keyCode === 9){
if(targ.id === 'select_element' && targ.options[targ.selectedIndex].value !== ''){
this.__changeStateDetector(e);
this.loadingElement.focus();
}
}
}
// content changer (also used for clicks)
__changeStateDetector:function(e){
// only execute if there is a state change
if(this.selectedState !== e.target.options[e.target.selectedIndex].rel){
// get state name and file path
var stateName = e.target.options[e.target.selectedIndex].rel;
var stateFile = e.target.options[e.target.selectedIndex].value;
// get the state file
this.getStateFile(stateFile);
this.selectedState = stateName;
}
}
getStateFile:function(stateFile){
new Ajax.Request(stateFile, {
method: 'get',
onSuccess:function(transport){
// insert markup into container
var markup = transport.responseText;
// NOTE: select which part of the fetched page you want to insert,
// this code was written to grab the whole page and sort later
this.resultsContainer.update(markup);
var timeout = setTimeout(function(){
// focus on new content
this.resultsContainer.focus();
}.bind(this), 150);
}.bind(this)
});
}
It really depends whether you are just adding some messages or replacing large parts of the page.
Messages
There are Aria Live Regions, which announce any change to their contents. This is very useful for status messages and sometimes even used with visually hidden live regions to only address screen reader users.
<button onclick="document.querySelector('#statusbar').innerHTML = new Date().toString()">Update</button>
<div id="statusbar" aria-live="assertive"></div>
The aria-live attribute establishes a live region and its value is a politeness setting, which regulates how likely it is the change will interrupt what is currently being spoken by the screen reader.
Another classic example is inline validation of form fields, where the alert role, a live region, is used to immediately announce the error message to the user:
<label>Day of the week we hate
<input type="text" aria-describedby="error">
</label>
<div role="alert" id="error" hidden>only Monday is permitted</div>
Large Parts of Content
When JavaScript is changing large parts of the site, like in single page applications, putting everything inside a live region would be overkill and actually very annoying.
To let the user know that content changed after activating a trigger, two approaches exist:
A new state of the trigger is announced, implying that the user can simply continue reading to find the new content
Focus is put either onto the element who’s content changed, or on the first interactive element inside
Simply read on
The first case would be applied if the role of the trigger (or other status information) makes it clear that a content change will happen, so it’s expected.
A classic example is the accordion. It has aria-expanded state, which communicates whether its contents are currently visible or not. If they are, the user will simply continue reading, because contents should follow immediately after.
toggleAccordion = e => {
const target = document.getElementById(e.currentTarget.getAttribute('aria-controls'));
e.currentTarget.setAttribute('aria-expanded', ! target.toggleAttribute('hidden'));
}
<!-- soon to be replaced by <details> and <summary> -->
<button aria-expanded="false" aria-controls="accordion-content" onclick="toggleAccordion(event)">2.1 First Rule of ARIA Use</button>
<blockquote id="accordion-content" hidden>
<p>If you can use a native HTML element [HTML51] or attribute with the semantics and behavior you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so. […]</p>
</blockquote>
Put focus on the new content
In the second case focus is set programmatically elsewhere, so that element will be announced. This is particularly helpful if it’s parent elements have grouping roles, so their names will be announced as well, as in the case of a modal dialog.
Another example would be a single page application’s navigation, where the single navigation items are still navigated by means of tab.
To be able to programmatically focus a non-interactive element, but not manually, tabindex="-1" is necessary. Focussing the headline is a best practice.
/* some sort of SPA router */
document.querySelectorAll('nav a').forEach(a => a.addEventListener('click', e => {
// hide all visible contents
document.querySelectorAll('main > :not([hidden])').forEach(c => c.hidden = true);
document.querySelectorAll('[aria-current]').forEach(c => c.removeAttribute('aria-current'));
// show selected content
const content = document.querySelector(e.currentTarget.getAttribute('href'));
content.hidden = false;
content.querySelector('h1').focus();
e.currentTarget.setAttribute('aria-current', 'page');
}));
a[aria-current] { font-weight: bold }
<nav>
<ul>
<li>Page 1</li>
<li>Page 2</li>
</ul>
</nav>
<main>
<div id="page-1">
<h1 tabindex="-1">Page 1</h1>
<p>Many lines of content to follow</p>
</div>
<div id="page-2" hidden>
<h1 tabindex="-1">Page 2</h1>
<p>Many lines of content to follow</p>
</div>
</main>