Tree Menu? In JS? - javascript

I need a tree menu. But instead of a listview where you expand/collapse i need a dropdown box with the list and when you click on a element i need the box to update (with the first entry being 'Back') so the menu stays in a neat little dialog.
Does this menu have a name? Does anyone know where i can get code to do this?

I can think of several jQuery plugins which would soot your purposes. However, I would recommend jQuery iPod Style Drilldown Menu (Newer Version), which is exactly what it sounds like. The dropdown box updates in place, uses a cool sideways slide animation, and includes a "Back" button (as you desired). Finally, if you don't want any animation, you can try tweaking the plugin's many options. Setting crossSpeed to 0 may work, for example.

Adam is right, jQuery offers an assortment of menu's which you could use. Really though, this is a somewhat trivial problem, the code to write it would take up about 1/10th the space that jQuery's code will. So if possible I would say write it without jQuery.
The most effective method would be to do it JS OOP (Javascript Object-Oriented), but understandably this is a confusing topic.
Basically you just want something like:
function drillDown(){
//Any code that multiple drilldowns
// might need on the same page goes here
//Every instance of a drillDown will
// instantiate a new set of all functions/variables
// which are contained here
//A reference to the parent node the dropdown is placed in
this.parent;
//A reference to the div the dropdown is incased in
this.object;
//Returns a reference to this object so it can be
// stored/referenced from a variable in it's
// superclass
return this;
}
//Prototype Functions
//prototypes are shared by all
// instances so as to not double up code
//this function will build the dropdown
drillDown.prototype.build = function(parent){
//Too lazy to write all this, but build a div and your select box
// Add the select box to the div,
// Add the div to the parent (which is in your document somewhere)
var divEle = document.createElement('div');
var inputBox = document.createElement('input');
//code code code
divEle.appendChild(inputBox);
parent.appendChild(divEle);
}
//this function loads the newest dataset of
drillDown.prototype.loadNewDataSet = function(data){
//first clear out the old list
// remember we have a reference to both the
// 'object' and 'parent' by using
// this.object and this.parent
//load the data, we are going to use the text from
// the select boxes to load each new dataset, woo eval();
// If you didn't know, eval() turns a string into JS code,
// in this case referencing an array somewhere
var dataSet = eval(data);
//then loop through your list adding each new item
for(item in dataSet){
//add item to the list
//change the .onClick() of each one to load the next data set
// a la ->
selectItem.onClick = function(){this.loadNewDataSet(item);};
//if you name your datasets intelligently,
// say a bunch of arrays named for their respective selectors,
// this is mad easy
}
}
//Then you can just build it
var drillDownBox = new drillDown();
drillDownBox.build(document.getElementsByTagName('body')[0]);
drillDownBox.loadNewDataSet("start");
//assuming your first dataset array is named "start",
// it should just go
And by the way, Adam also said it, but wasn't explicit, this is refered to as a drill-down.

Related

Next/Prev buttons to step through div contents

First of all a disclaimer, I'm not a dev. I'm halfway through The Odin Project and have covered some HTML and CSS, but, have not yet started on JS. In order to help with my learning I've created my own blog. My aim is for each blog post to have its own stylesheet (so with each new post I learn a little more about CSS).
Anyway, I plan to write a post about the benefits of using an eReader, specifically the Kindle. I've styled the page to look like a Kindle Oasis, and I'd like the reader to be able to step through the article contents via the Kindle's next/prev buttons, but, as I'm not a dev, this is where I'm stuck. Via Stack overflow I've managed to add some JS that will display page 1, 2 and 3 via dedicated buttons for each dive element, but, what I really need is to step through x number of pages via the prev/next buttons.
Here's what I have so far: https://codepen.io/dbssticky/pen/yLVoORO. Any help would be much appreciated. What I should do of course is finish The Odin Project and come up with a solution on my own, but, I'd really like to get this Kindle article published sooner rather than later. Hence my rather cheeky request for assistance.
Here's the JS I'm currently using:
function swapContent(id) {
const main = document.getElementById("main_place");
const div = document.getElementById(id);
const clone = div.cloneNode(true);
while (main.firstChild) main.firstChild.remove();
main.appendChild(clone);
}
You have the right idea and it just needs a few adjustments to get the previous/next functionality.
Currently your div IDs are following the format operation1, operation2, and so on. Since you want the previous/next functionality you'll need to change your 'swapping' function, which currently takes the full ID, to use the numeric portion only.
Add a new function which appends the number to 'operation' instead of using the whole thing:
function goToPage(pageNumber){
const main = document.getElementById("main_place");
const div = document.getElementById("operation" + pageNumber);
const clone = div.cloneNode(true);
while (main.firstChild) main.firstChild.remove();
main.appendChild(clone);
}
And then change your Page 1/2/3 buttons to use goToPage(1), goToPage(2) and so on.
Now for the previous/next functionality you'll need a way to track which page you're on, so that you can figure out which page to load.
Add a variable at the top (outside functions)
var currentPage = 0;
Then add a line in your goToPage function to track the page you're on.
currentPage = pageNumber;
Now that you're tracking you can add a previous and next function.
function goNextPage(){
goToPage(currentPage-1);
}
function goPreviousPage(){
goToPage(currentPage+1);
}
Then call it from the previous and next buttons.
<button onClick="goNextPage()" class="next-button"></button>
<button onClick="goPreviousPage()" class="previous-button"></button>
Here's a codepen: https://codepen.io/srirachapen/pen/WNZOXQZ
It's barebones and you may have to handle things like non existent div IDs.
HTML
<button class="next-button" onclick="nextContent()"></button>
<button class="previous-button" onclick="prevContent()"></button>
JS
var pageid = 0;
var maxpage = 3;
function nextContent() {
if(pageid == maxpage) return
pageid++
swapContent(`operation${pageid}`)
}
function prevContent() {
if(pageid == 1) return
pageid--
swapContent(`operation${pageid}`)
}
you can try this to switch between pages. But you may need to edit the "swapContent" method more sensibly.
Track the Current Page
Whatever solution you use to render pages & links (manual hardcoded links & content vs externally-stored & auto-generated), one thing is unavoidable: You need to track the current page!
var currentPage = 0
Then, any time there's a page change event, you update that variable.
With the current page being tracked, you can now perform operations relative to it (e.g. +1 or -1)
I'd suggest making a goToPage(page) function that does high-level paging logic, and keep your swapContent() function specifically for the literal act of swapping div content. In the future, you may find you'd want to use swapContent() for non-page content, like showing a "Welcome" or "Help" screen.
Example:
function goToPage(page) {
// Update `currentPage`
currentPage = page
// ... other logic, like a tracking event or anything else you want you occur when pages change
// Do the actual content swap, which could be your existing swapContent()
swapContent('operation'+page)
}
You'd invoke the function like so:
goToPage(3) // Jump to a specific page
goToPage(currentPage + 1) // Go to the next page
goToPage(currentPage - 1) // Go to the prev page
You can make separate helper functions like "goToNextPage()" if you desire, but for sure you start with a fundamental page-change function first.

TinyMCE retrieving elements using DomQuery

I'm dealing with TinyMCE to create a WYSIWYG editor, but there is a problem and now I'm stuck on it.
I need to create a system where users are allowed to create specific documents, each devided into sections i.e. a wrapper. Inside every section there are textual and block elements (p, table, img and so on).
Now, the problem is: when a new section needs to be created I'm using the following code
function insertRawSection () {
// Close the current section and open the next one
Editor.execCommand('mceInsertRawHtml',false,`</section><section><h1>${ZERO_SPACE}</h1>`)
}
This code works, but the real problem comes out when I need to move the cursor at the start of the new h1 element.
I can't retrieve the new heading because if I look for it with DomQuery it doesn't appear.
The code I use to lookup the h1 element is the following
function insertRawSection () {
// Close the current section and open the next one
Editor.execCommand('mceInsertRawHtml',false,`</section><section><h1 data-pointer>${ZERO_SPACE}</h1>`)
// Lookup the last inserted heading
console.log($('[data-pointer]'))
}
Note: The variable $ is not JQuery but is TinyMCE.DomQuery (everything is correctly setted up)
The log print only the previous existent headings, but not the last one. Probably there is somethings like a refresh to execute, but what i have to do in order to "communicate" between this command and the DomQuery APIs?
Instead of using mceInsertRawHtml same can be achieved using dom methods of tinymce.
var ed = tinymce.activeEditor;
var currentNode = ed.selection.getNode();
var newEle = ed.dom.create('section', {}, '<h1></h1>');
ed.dom.insertAfter(newEle, currentNode);
ed.selection.select(newEle.firstChild.firstChild);
ed.selection.collapse(false);
ed.focus();
Once the element is created same can be selected using dom methods to place the cursor at the begining/ending of the element.

select / cut / pasteInto when window not visible

I'm working on a script where I take a business card design and use it to generate a sheet of paper that has ten cards on it to match a template to print temporary cards. The tricky part here is the bleeds; they'll overlap down the middle so I need to make clipping masks for each one.
I came up with a system where I made the frames that would become the clipping masks, duplicated and moved the cards where they need to go, and then more or less did the following:
dupCard[i].select();
app.cut();
frameGroupFront[i].select();
app.pasteInto();
This works great. But because it's a little resource-intensive, I tried to hide the working file upon creation and use workingFile.windows.add(); at the end as I've done in the past. But when there's no window, select() doesn't work! I get error 90886 stating that "No document windows are open."
How can I select the items I want so that I can cut and paste it without having a visible window? If not possible, is there an alternate solution to the problem?
EDIT:
I was asked to provide a scripting sample, so here's the most basic sample I can furnish:
var newPage = app.documents.add();
var myRectangle = newPage.rectangles.add({geometricBounds:[1, 1, 5, 5]});
var myRectangle2 = newPage.rectangles.add({geometricBounds:[1, 1, 3, 3]});
myRectangle.select();
app.cut();
myRectangle2.select();
app.pasteInto();
This script works. But, take the first line and do app.documents.add(false) instead, and it doesn't work because no document window is open. In this example, I'd like to be able to get the one rectangle inside the other with no window visible.
Instead of using copy and paste, you can manipulate the rectangle objects themselves like this:
var doc = app.documents.add(); // Add a new doc
var page = doc.pages[0]; // Get the first page
var rect = page.rectangles.add({geometricBounds:[30,30,6,6]}) // Make a new rect
var rect2 = rect.rectangles.add({geometricBounds:[20,20,6,6]}); // Add a new rect inside
This can all be done without the window being open since you're manipulating the objects directly. Hope this helps!

Is Jquery $(this) broken by jqgrid gridunload method?

I expect the following code to unload a javascipt jqgrid, then load another grid with different options, including different columns
//onload
(function($)
$.fn.myGridFn = function(options){
$(this).jqGrid('GridUnload');
$(this).jqGrid(options.gridoptions);
//....
$('#select').change(function(){
switch($(this).val())
{
case 'grid1':
$('#grid').myGridFn({gridoptions:{/*grid1 options*/}});
break;
case 'grid2':
$('#grid').myGridFn({gridoptions:{/*grid2 options*/}});
break;
}
});
})(jQuery);
//...
<table id="grid"></table>
What I get is the grid unloading, then I have to change the selection in the select element and back again to load the new grid.
Updated:
If I replace the $(this) in the plugin with the actual element selector $('#grid') - it works just fine, I cant do this in my real app because the plugin is used by several other table elements and grids
Cleaned up for future readers:
So here's a sort of working fiddle: http://jsfiddle.net/s3MsW/10/
I say "sort of" because the underlying code is suspect (jqGrid itself). But we'll get there in a moment... first thing: if you log "this" for the plugin, it's actually the jQuery object, not the node. Theoretically we can replace $(this) in your original code with this and all should work.
Except not.
You can in fact use this to unload the Grid, but then the function leaves this as a reference that does not point to the table on the rendered page. There are ways to show that the old node is still around ( http://jsfiddle.net/s3MsW/8 was a test ) but suffice it to say it can no longer be used to render a new table to the page proper.
There's no real choice except to cache the selector string and re-select the clean table (ie. create a new jQuery object) from scratch:
$.fn.myGridFn = function(options){
var theId = this.selector;
this.jqGrid('GridUnload'); // reference works for now
$(theId).jqGrid(options); // reference is broken, so re-select with cached ID
}
If you're conscientious about memory usage, you probably want to destroy this (the ghost node), but there's probably no real harm just keeping it around.
It seems to me that you should just save $(this) in a variable like $this and use it later. The problem is just that inside of
$('#select').change(function(){/*here*/}); // another value of this
so you should do
(function($)
$.fn.myGridFn = function(options) {
var $this = $(this), selector = $this.selector;
$this.jqGrid('GridUnload');
$this = $(selector); // reset $this value
...
$('#select').change(function() {
switch($(this).val()) { // here is $('#select')
case 'grid1':
$this.myGridFn({gridoptions:{/*grid1 options*/}});
...
Additionally one use typically start the body of plugin with
return this.each( function() { ...
to be sure that your plugin works also in the case of usage like $(".myGridClass").myGridFn(...) where one can have more as one element in wrapped set $(".myGridClass").
This issue stumped and the answer above was right on.
I kept trying to execute the following:
this.jqGrid('GridUnload')
this.('getGridParam'); /* Still returning all the parameters for the grid. */
Instead I did:
var $t = $(this.selector);
$t.jqGrid('GridUnload');
$t = $(this.selector);
$t.jqGrid('getGridParam'); /* Now empty */
I think you should try
$('#select option:selected).val()// gives the value of the selected option.
$('#select option:selected).text()// gives the text of the selected option.
instead of
$(this).val()
in the parenthesis of switch

jquery checkbox slow, not sure how to fix

I have used firebug and IE profilers and can see what function in my code is causing the slowness. Being new to jquery, the recommendations that I have read online are not clear to me. I have made an example page that shows the slow behavior when you check or uncheck a check box. No surprise that this is fast using Chrome.
The function that is slow can be found on line 139.
$('.filters input').click( function()
JSFiddle can be found here
The code is 122 KB and can be found here
UPDATE: if you know of any examples online that are similar in function and faster, please share.
i had a brief look through your code, but it was very hard to follow. it seemed as if you were looping through things many many times. i used a much simpler approach to get the list of all states.
your approach was
* make a massive string which contained every class (possibly repeated multiple times)
* chop it up into an array
* loop through the array and remove duplicates
i simply took advantage of the fact that when you select something in jQuery you get a set rather than a single item. you can therefore apply changes to groups of object
$(document).ready(function () {
//this will hold all our states
var allStates = [];
//cache filterable items for future use
var $itemsToFilter = $(".filterThis");
//loop through all items. children() is fast because it searches ONLY immediate children
$itemsToFilter.children("li").each(function() {
//use plain ol' JS, no need for jQuery to get attribute
var cssClass = this.getAttribute("class");
//if we haven't already added the class
//then add to the array
if(!allStates[cssClass]) {
allStates[cssClass] = true;
}
});
//create the container for our filter
$('<ul class="filters"><\/ul>').insertBefore('.filterThis');
//cache the filter container for use in the loop
//otherwise we have to select it every time!
var $filters = $(".filters");
// then build the filter checkboxes based on all the class names
for(var key in allStates) {
//make sure it's a key we added
if(allStates.hasOwnProperty(key)) {
//add our filter
$filters.append('<li><input class="dynamicFilterInput" type="checkbox" checked="checked" value="'+key+'" id="filterID'+key+'" /><label for="filterID'+key+'">'+key+'<\/label><\/li>');
}
}
// now lets give those filters something to do
$filters.find('input').click( function() {
//cache the current checkbox
var $this = $(this);
//select our items to filter
var $targets = $itemsToFilter.children("li." + $this.val());
//if the filter is checked, show them all items, otherwise hide
$this.is(":checked") ? $targets.show() : $targets.hide();
});
});
FIDDLE: http://jsfiddle.net/bSr2X/6/
hope that's helpful :)
i noticed it ran quite a bit slower if you tried to slideup all the targets, this is because so many items are being animated at once. you may as well just hide them, since people will only see the ones at the top of the list slide in and out of view, so it's a waste of processor time :)
EDIT: i didn't add logic for show all, but that should be quite a trivial addition for you to make if you follow how i've done it above
You could use context with your selector:
$('.filters input', '#filters_container').click(function()...
this limits the element that jQuery has to look in when selecting elements. Instead of looking at every element in the page, it only looks inside your $('#filters_container') element.

Categories

Resources