DataTables - Open child rows on top of parent row - datatable

I need to implement functionality where child row will slide and open on the top of the parent row instead of bottom. Could anyone please help.
// Add event listener for opening and closing details
$('#example tbody').on('click', 'td.details-control', function () {
var tr = $(this).closest('tr');
var row = table.row( tr );
if ( row.child.isShown() ) {
// This row is already open - close it
$('div.slider', row.child()).slideUp( function () {
row.child.hide();
tr.removeClass('shown');
} );
}
else {
// Open this row
row.child( format(row.data()), 'no-padding' ).show();
tr.addClass('shown');
$('div.slider', row.child()).slideDown();
}
} );
https://jsfiddle.net/nnb97rh9/3/

The problem is that HTML shows a nested table below the row it's nested in. The datatables routine that you're using sets up a nested table when you push the green button, so you're coming up against an HTML limitation.
The only way that you could maybe do this is very messy: on clicking a green button, go to the row prior to the current one, and call the format function on it. (Of course, you'd have to find a way to pass row.data from the current row rather than the previous one, after moving to the previous one. And there would be other messiness that I'm not going to contemplate.)
That begs the question of how to handle the first row, since there isn't one prior to it; you'd probably have to come up with a different function for that. That's all counter-intuitive and illogical: you don't really want to nest a table inside a row with data that's unrelated to it.
Which brings us to why you want to do this in the first place. You're almost certainly going to confuse your users: you're basically doing this differently from 99.99999% of the rest of the world, thereby depriving your users of their previous experience and making your UX counter-intuitive. As a comparison, what if you wanted a "dropup" list instead of the more usual dropdown list? Wouldn't that confuse most users?
My suggestion is to consider abandoning this idea. Take a few steps back and rethink what you want to accomplish, and then see if you can do things in a manner that's closer to established UI behavior. Users prefer to work with UIs that leverage their past experience working with UIs, so they don't have to put in extra time to learn a new way of doing things. Don't ask them to find new ways to do things they already know how to do, or they won't use your application.

Related

Loop through drop down with dynamic html table

I'm hard stuck with this one so any advice welcome!
Ive been trying to create a flow that goes to this website https://dlv.tnl-uk-uni-guide.gcpp.io/ and scrapes the data from each table in the Subject Areas drop down list. My knowledge of HTML is sketchy at best but from what I understand it's a dynamic html table that just refreshes with new data rather than going to a new url. I can extract the subject list as a variable and in my head i think i just need to add this to a UI selector action but despite numerous attempts i've got absolutely nowhere. Anyone got any ideas as to how i could fix this or work around?
Because it is not a conventional drop-down using the "Set drop-down list value on web page" doesn't work all that well.
You can use a bit of javascript and variables for this.
Hit F12 to show developer tools, you will see there is a list of hidden items with the class class="gug-select-items gug-select-hide" you will use this in the javascript.
Then add a 'Press button on web page' function and add the 'drop-down' element, which is a <div>
Then edit the element selector and change it to text editor.
then change the selector to make use of the nth-child(0) selector but use a variable for the index.
so it looks something like #gug-overall-ranking-select > div.gug-select-items > div:nth-child(%ddIdx%)
Use the "Run JavaScript function on web page" function to get the number of options available to the drop-down. (child elements)
The returned result is text, so convert it to a number that can be used in the loop.
function ExecuteScript() { /*your code here, return something (optionally); */
var firstDDlist = document.querySelector("#gug-overall-ranking-select > div.gug-select-items.gug-select-hide");
return firstDDlist.children.length;
}
In the loop each element will be pressed and cause the table to reload.
The table data extraction can then also be done in the loop, but that this code only shows the looping through the options.
The full flow 'code' (copy this and paste it in power automate).
WebAutomation.LaunchEdge.LaunchEdge Url: $'''https://dlv.tnl-uk-uni-guide.gcpp.io/?taxonomyId=36&/#gug-university-table''' WindowState: WebAutomation.BrowserWindowState.Normal ClearCache: False ClearCookies: False WaitForPageToLoadTimeout: 60 Timeout: 60 BrowserInstance=> Browser
WebAutomation.ExecuteJavascript BrowserInstance: Browser Javascript: $'''function ExecuteScript() { /*your code here, return something (optionally); */
var firstDDlist = document.querySelector(\"#gug-overall-ranking-select > div.gug-select-items.gug-select-hide\");
return firstDDlist.children.length;
}''' Result=> numberOfItems
Text.ToNumber Text: numberOfItems Number=> itemCount
LOOP ddIdx FROM 1 TO itemCount STEP 1
WebAutomation.PressButton.PressButton BrowserInstance: Browser Control: appmask['Web Page \'h ... sity-table\'']['Div \'gug-select-selected\''] WaitForPageToLoadTimeout: 60
END
It should end up looking like this:
Flow running:
With using Power Automate Desktop (PAD), the goal is to be a low-code solution. Of course knowing HTML is a bonus and will help you on tricky webpages or problems, but not knowing much is alright usually. I'm not really comfortable going to that web page you shared but you could try the below option.
PAD has a built in function in the action pane:
'Browser automation' > 'Web data extraction' > 'Extract data from web page'
Try using that and when asked to add UI Element select the table/dropdown list to see what information you get back. If that doesn't work you might need to try out JavaScript or another method.

How to find the highest attribute in a dataset, then display the "name" associated with that

I'm still relatively new to programming and I have a project I am working on. I am making a staff efficiency dashboard for a fictional pizza company. I want to find the quickest pizza making time and display the time and the staff members name to the user.
With the data charts it has been easy. Create a function, then use dc, e.g dc.barChart("#idOfDivInHtmlPage")
I suspect I might be trying to be too complicated, and that I've completely forgotten how to display any outputs of a js function to a html page.
I've been using d3.js, dc.js and crossfilter to represent most of the data visually in an interactive way.
Snippet of the .csv
Name,Rank,YearsService,Course,PizzaTime
Scott,Instore,3,BMC,96
Mark,Instore,4,Intro,94
Wendy,Instore,3,Intro,76
This is what I've tried so far:
var timeDim = ndx.dimension(function(d) {
return [d.PizzaTime, d.Name]
});
var minStaffPizzaTimeName = timeDim.bottom(1)[0].PizzaTime;
var maxStaffPizzaTimeName = timeDim.top(1)[0].PizzaTime;
}
then in the html
<p id="minStaffPizzaTimeName"></p>
<script type="text/javascript" src="static/js/graph.js">
document.write("minStaffPizzaTimeName");
</script>
You are surely on the right track, but in javascript you often have to consider the timing of when things will happen.
document.write() (or rather, anything at the top level of a script) will get executed while the page is getting loaded.
But I bet your data is loaded asynchronously (probably with d3.csv), so you won't have a crossfilter object until a bit later. You haven't shown these parts but that's the usual way to use crossfilter and dc.js.
So you will need to modify the page after it's loaded. D3 is great for this! (The straight javascript way to do this particular thing isn't much harder.)
You should be able to leave the <p> tag where it is, remove the extra <script> tag, and then, in the function which creates timeDim:
d3.select('#minStaffPizzaTimeName').text(minStaffPizzaTimeName);
This looks for the element with that ID and replaces its content with the value you have computed.
General problem solving tools
You can use the dev tools dom inspector to make sure that the p tag exists with id minStaffPizzaTimeName.
You can also use
console.log(minStaffPizzaTimeName)
to see if you are fetching the data correctly.
It's hard to tell without a running example but I think you will want to define your dimension using the PizzaTime only, and convert it from a string to a number:
var timeDim = ndx.dimension(function(d) {
return +d.PizzaTime;
});
Then timeDim.bottom(1)[0] should give you the row of your original data with the lowest value of PizzaTime. Adding .Name to that expression should retrieve the name field from the row object.
But you might have to poke around using console.log or the interactive debugger to find the exact expression that works. It's pretty much impossible to use dc.js or D3 without these tools, so a little investment in learning them will pay off big time.
Boom, finally figured it out.
function show_fastest_and_slowest_pizza_maker(ndx) {
var timeDim = ndx.dimension(dc.pluck("PizzaTime"));
var minPizzaTimeName = timeDim.bottom(1)[0].Name;
var maxPizzaTimeName = timeDim.top(1)[0].Name;
d3.select('#minPizzaTimeName')
.text(minPizzaTimeName);
d3.select('#maxPizzaTimeName')
.text(maxPizzaTimeName);
}
Thanks very much Gordon, you sent me down the right path!

ckeditor disable element, prevent user from adding content to it

I've added a plugin that allows the user to add a specially styled div via a dialog. The issue now is, this element should not be clickable inside the edtior. The problem is the users manage it to click inside the div and enter text there and by this screw it up.
I've already spent some time searching the documentation but couldn't find the right approach to do this yet. I'm not asking for code, just some advice how to do it, a pointer to the right API method would be good enough for me. I guess I can somehow access the elements or intercept an users click and prevent them from adding something to my element somehow, I just couldn't yet figure out how to do it.
Use the Widget System.
Widget Tutorial.
Demos.
I've finally managed to get this done by making the elements content not editable. When I create the element in my dialog:
hrElement.setAttribute('contenteditable', false);
When loading the plugin:
init: function (editor) {
editor.on('contentDom', function () {
var stiching = (this.document.getElementsByTag('div'));
console.log(stiching);
for(var i=0;i<stiching.count();i++){
if (stiching.getItem(i).hasClass('stitching')) {
stiching.getItem(i).setAttribute('contenteditable', false);
}
}
});
}
I'm pretty sure this is not the most best solution (don't like to iterate over the elements) but at least it works for me now. Any suggestions how to improve it for future cases are welcome.

How to code in LimeSurvey so that a new row appears only when the previous row has been filled

I am using LimeSurvey and I want to include a question where the respondent can include up to 30 names as answer. However, I don't want to initially present the respondent with 30 boxes as it is overwhelming and requires a ton of scrolling to proceed if you only have a few names to enter. Is is possible to code the question so that a new box appears only after the previous box has been filled? Thanks.
Here is another approach that uses buttons to add/remove the array rows - http://manual.limesurvey.org/Workarounds:_Manipulating_a_survey_at_runtime_using_Javascript#Variable_Length_Array_.28Multi_Flexible_Text.29_question
Cheers
EDIT: This answer was written because I could not find the answer presented by tpartner beforehand. The main difference is that mine is based on filling out the previous row and tpartner's on buttons to add or remove rows.
The following code should work for all single-choice arrays (e.g. 5-point scale array) and is adaptable to other types if you know some Javascript/jQuery. I want to do more like that - just not today. So feel free to ask for implementations for other question types.
The code can be added in the beginning of the template.js file using the template editor. The variables "quest" and "first" have to be adapted based on your survey.
//Function to only display a new row if there is an answer in the previous row
//NOTICE: Rows which are reset to "No answer" will not be hidden
//NOTICE: This scipt was written based on LimeSurvey 2.00+ build 131107
//NOTICE: It only works for single-choice arrays (e.g. 5-point scale array) or multiple short texts
//BEGIN
$(document).ready( function() {
//SGQ code of the question to apply this to
var quest = "12345X1234X12345";
//A(nswer) code of the first row
var first = "1";
//hide all rows except the first
$("tr[id^='javatbd" + quest + "']").css("display","none");
$("tr[id='javatbd" + quest + first + "']").css("display","table-row");
//display rows if previous is answered
$("[name^='" + quest + "']").change(function() {
if(this.value.trim().length >= 1)
$("tr[id='javatbd" + this.name + "']").next().css("display","table-row");
});
});
//END
Best regards
Which question type are you planning to use? Your best bet, according to me, should be to use Multiple Short text type question and create 30 text boxes. You can then use javascript to hide these text boxes and show them as soon as the previous text box gets some value as input.
cheers!
With version 2.x (not sure which version starts allowing this, I am running 2.7 and it works there), this functionality is inbuilt via the relevance equation and doesn't require coding. Just enter !is_empty(questioncode_Code) in the relevance equation for the text box you want to let appear. Code is the code of the field one above which triggers the appearance.

Tying a data source from dojo.xhrPost to a text box for autocomplete

I inherited a web app that uses Dojo 1.5 and the template toolkit. I am enjoying learning dojo but it's at a slow pace.
Initially when bringing up our web form, we'll have a list of files on the right side of the page like so....
AAA_text
BBB_text_1
BBB_text_2
CCC_text
....
....
On the left side we have a search box that asks for the subset of file to use. Normally we would just type in "AAA" and then the div on the right side would find those files that match and display them after you press the "Search" key below the box.
What we are looking to do is to eliminate the "Search box" and have the list of files matching "AAA" to come up in the right side div as "AAA" is being typed, (or "BBB" or "CCC", etc).
I suppose in a nutshell it's the equivalent having the "Search" button pressed after every key is typed in the Search box.
Does this sound like a realistic goal or even possible? The code itself uses a ton of Template Tookit so I'm not looking to do any major rewrite.
If I am not making myself clear, let me know. I can elaborate for clarity. Many many thanks! Janie
EDIT: OK, I have solved a good deal of my problem so far and as it turns out, as so many of these things have a propensity to do, that what I am really needing is to get clear on how to make autocomplete work. Which is to say that I have a data source for my text box but not really sure how to tie it to the text box. I have a dojo.xhrPost routine that can handle grabbing the values.
It looks like this....
dijit.byId('files_matching').getValue(),
Googling dojo autocomplete examples gives me a zillion links and none of which are proving helpful. So I suppose my questions have transitioned to....
1. Can you even use autocomplete on a mere text box (I've seen links that say that you can only use it on combo boxes)
2. Is there a link out there somewhere that describes/shows in detail how to tie a dojo text box to a data source using dojo.xhrPost.
I am so close to solving this and I still seem to have a gaping chasm in front of me.
It's difficult to say for sure without seeing your code but if you don't have one already, I would recommend to create an ItemFileReadStore or something similar to start with. That way you can query that store locally on the client without having server requests after every key stroke.
It could look something like this:
var file_store = new dojo.data.ItemFileReadStore({ data: {
items: [{ name:"AAA_text" },
{ name:"AAA_text_1" },
{ name:"BBB_text_2" }]
}});
When you have that in place you can call a function from your text input's onChange event:
<input type="text" onchange="query_store(this.value);" />
And then you handle to actual query from the function called from the onchange event:
var query_store = function(search_for) {
var my_query = { name: search_for+"*" }; // * for wildcard match
completed = function(items, result){
for(var i = 0; i < items.length; i++){
var value = file_store.getValue(items[i], "name");
alert(value); // Instead of alert, you can save the values to your div.
}
};
file_store.fetch({ query: my_query, onComplete: completed });
}
A lot of good information about this can be found here
Hope this is at least a little helpful.

Resources