I am trying to enhance my page with a jquery right mouse menu, but am having trouble building the correct structures to populate it easily.
Currently my page contains (among other things) a list of items for the user to review. (an html table) Based on the users role, and the current state and context of the row, the user may take one of various actions on each row of data. (approve, reject, refer it to someone else, ect.) My ASP.Net page handles this by setting the visibility of an imagebutton within the row to true, if the option is available. I can control the Cssclass of each button, and am setting the class of for example the "approve" button to “approvebtn”.
Now I want to enhance my site with a right menu.
I am extending my site with Cory S.N. LaViska’s jQuery Context Menu Plugin -
http://abeautifulsite.net/notebook/80
This plugin allows the default right mouse behavior for any elelement to be overridden with a user controlled context menu. The menu is inserted into your page as an unordered list and becomes visible when it is needed.
<ul id="rightMenu" class="contextMenu">
<li class="details">Details </li>
<li class="addnote">AddNote </li>
<li class="listnote">ShowNotes </li>
<li class="approve">Approve </li>
<li class="reject">Reject </li>
<li class="release">Release </li>
<li class="takeover">Takeover </li>
</ul>
Your app gets a callback when something on the right menu is clicked, and you can interrogate the action (the bogus href element) to see which item it was.
I really like this menu because it is simple to use and is completely CSS styled.
However, I need to do something that this plugin does not nativly seem to support. I need to change which items are available on the menu from row to row. Basically if an Imagebutton (for say approve) is avaiable in the row, then its corrisponding menu item should exist as well.
I was able to gain access to the menu just before it is displayed by altering the plugin slightly, to call my function right before the menu is displayed.
This works, but the logic I had to write seems so brute force, that there must be a better way….
In my callback:
function jimsbuggeredfunction(menu,el)
"el" is the element that was right clicked on (usually a table cell), and "menu" is the menu that this right click is bound to. (so I should be using that name and not hardcoding to #rightMenu')
So, the “if” line finds out if the table row containing the element that was “right clicked” contains a specific button (by its class name) if it does the menu item is enabled, otherwise it is disabled. This process continues for every menu item that I want to be flexable row-to-row.
function jimsbuggeredfunction(menu,el) {
if($(el).parents("tr:eq(0)").find('.approvebtn').length > 0)
$('#rightMenu').enableContextMenuItems('#approve');
else
$('#rightMenu').disableContextMenuItems('#approve');
if($(el).parents("tr:eq(0)").find('.rejectbtn').length > 0)
$('#rightMenu').enableContextMenuItems('#reject');
else
$('#rightMenu').disableContextMenuItems('#reject');
if($(el).parents("tr:eq(0)").find('.releasebtn').length > 0)
$('#rightMenu').enableContextMenuItems('#release');
else
$('#rightMenu').disableContextMenuItems('#release');
if($(el).parents("tr:eq(0)").find('.takeoverbtn').length > 0)
$('#rightMenu').enableContextMenuItems('#takeover');
else
$('#rightMenu').disableContextMenuItems('#takeover');
if($(el).parents("tr:eq(0)").find('.revertbtn').length > 0)
$('#rightMenu').enableContextMenuItems('#revert');
else
$('#rightMenu').disableContextMenuItems('#revert');
if($(el).parents("tr:eq(0)").find('.removebtn').length > 0)
$('#rightMenu').enableContextMenuItems('#remove');
else
$('#rightMenu').disableContextMenuItems('#remove');
if($(el).parents("tr:eq(0)").find('.addnotebtn').length > 0)
$('#rightMenu').enableContextMenuItems('#addnote');
else
$('#rightMenu').disableContextMenuItems('#addnote');
if($(el).parents("tr:eq(0)").find('.listnotebtn').length > 0)
$('#rightMenu').enableContextMenuItems('#listnote');
else
$('#rightMenu').disableContextMenuItems('#listnote');
};
There must be a better way to set this up, so that it also just ignores menu items that I want to display all of the time) but it is escaping me at the moment. Is there a better way to accomplish this?
Thanks,
Jim
I would find some way to create a mapping between the two IDs and some more systematic way of finding the relevant button. For example, if the button always belongs inside a certain cell that has a class, let's say "buttonclass", then something like this should work:
var mapping = {
takeoverbtn: '#takeover',
listnotebtn: '#listnote'
// ...
};
function jimsbuggeredfunction(menu,el) {
var buttontype = $(el).parents("tr:eq(0)").find('.buttonclass').children().attr("class");
$('#rightMenu').disableContextMenuItems(mapping[buttontype]);
}
My jQuery is a little rusty, there's probably a cleaner way of retrieving the buttontype, but that general idea ought to work.
Related
This is trying to hide a checkout payment option from appearing when specific items are in cart (shown on the same page). If the text "BULK" appears in the cart/page to hide a list option based on its data attribute? I've tried learning js and the last 2 hours of watching a course, I understand more but this still seems more advanced than what I can do right now. Would a boolean argument using string.search and insert div.style.display "none"?
Cart example to search for text:
<h4 class="product-title optimizedCheckout-contentPrimary" data-test="cart-item-product-title">BULK Powder 50 lbs.</h4>
Payment option:
<li class="form-checklist-item optimizedCheckout-form-checklist-item" data-test="accordion-item_paypalcommerce">
Once you have a reference to the item (or items - same idea only in a loop) - read the text of the element. Using indexOf sounds reasonable to find a string inside another. And if all is well then just set display:none to the right payment option.
The javascript is basic, but you should also learn some about css selectors should you want to "select" the target elements using a different strategy.
var elem = document.querySelector(".product-title");
var bool = elem.innerText.indexOf('BULK')>=0
if (bool) {
var li = document.querySelector("li[data-test='accordion-item_paypalcommerce']");
li.style.display = 'none'
}
<h4 class="product-title optimizedCheckout-contentPrimary" data-test="cart-item-product-title">BULK Powder 50 lbs.</h4>
Payment options:
<li class="form-checklist-item optimizedCheckout-form-checklist-item" data-test="accordion-item_paypalcommerce">Cash</li>
<li class="form-checklist-item" data-test="accordion-item_paypalcommerce">Credit</li>
I'm very new to programming & working on creating a website for a work project.
In it, there will be a multi-level (w/sub-menus) vertical sidebar on each page.
The problem I'm facing is that every time a user clicks on one link, the sidebar resets to its original state & will have to redo the same thing & not very UX friendly.
I took the template of the accordian sidebar from here.
I've looked at various search results on both stack overflow & google, but can't seem to understand how to get it working to retain the state of the sidebar, regardless of how many levels are opened.
Can someone please help me with the JS code to get it working?
UPDATE:
Nathan, thanks for writing mate! I really appreciate the help.
So based on your suggestion, I've written the following (shoddy) code that injects the 'checked' attribute to the input element.
But it isn't transferring over to the new/redirected html page when a user clicks on one of the sub-menus. What am I missing here?
var menuIndex = -1;
//extract all the input elements
var inputs = document.querySelectorAll('.parent-menu');
//Find index of the element from the array that has "checked == true"
function indexFinder() {
for (var i = 0; i < inputs.length; i++) {
if (inputs[i].checked == true) {
menuIndex = i;
console.log(menuIndex);
}
};
}
//Function to set/inject the attribute
function attributeSetter() {
inputs[menuIndex].setAttribute('checked', 'checked')
}
//When a user clicks literally anywhere, it'll run the indexFinder function
//to check if any of the input elements were expanded (i.e. checked == true)
window.addEventListener('click', () => {
indexFinder();
});
//Run the attributeSetter function when a page loads
window.addEventListener('DOMContentLoaded', () => {
attributeSetter();
});
Welcome to the world of programming! Hopefully I can help you out a little!
So what you're asking is something that can easily get a little complicated.
In order to achieve what you're trying to do you need to specify how you want your menu to look on each individual page!
Allow me to present a few menu options for an imaginary site:
Home
Contact
Email
Mail
About
The Company
Our Owner
I've indented the page names based on how we want them to show up in our menu.
So for example you may click on "Contact" and it drops down with the Email and Mail options.
Well, if you take your regular code from that webpage and copy and paste it everywhere. Any time you reload a page (or travel to another page with the same code) it's gonna reset the code! Thus "closing" the menu. Think of it as some sort of multi-dimentional sci-fi. When you load a webpage, you are accessing the main flow of time, any time you make an update to that page it takes you to an alternate reality with that change! but once you reload the webpage you jump back to the main timeline as if you never made that change (when you get into more advanced web dev, this analogy will break down but it should work to help your understanding for now.)
So let's say I click on the Contact > Email option and it takes me to the Email page. Well, in order to make it seem like my changes to the menu bar (clicking "Contact" to expand the dropdown) are still active. I need to hardcode the change into the Email page!
Here's some sample code:
<nav class="nav">
<a class="navOption">Home<a>
<a class="navOption">Contact<a>
<div class="navDropdown">
<a class="navOption">Email<a>
<a class="navOption">Mail<a>
</div>
<a class="navOption">About<a>
<div class="navDropdown">
<a class="navOption">The Company<a>
<a class="navOption">Our Owner<a>
</div>
<nav>
By default the .navDropdown will be closed. However when we add a class to them .active they will expand! If this is my base menu, then how should I make it so that the "About" dropdown is expanded when you are on one of the About pages?
Simply by adding .active to that dropdown!
<nav class="nav">
<a class="navOption">Home<a>
<a class="navOption">Contact<a>
<div class="navDropdown">
<a class="navOption">Email<a>
<a class="navOption">Mail<a>
</div>
<a class="navOption active">About<a>
<div class="navDropdown">
<a class="navOption">The Company<a>
<a class="navOption">Our Owner<a>
</div>
<nav>
Now, my example is different from yours because it's meant more for JavaScript. However, you can use the same concept in your code too.
For you, instead of having a .active class to expand a dropdown menu. You are using a checkbox element! In your codem you have CSS which is checking to see if the checkbox is checked and then it is opening the dropdown menu if it is:
<input class="cd-accordion__input" type="checkbox" name ="group-1" id="group-1">
So, if we use this method on our example webpage. We could set it to be open by setting the checkbox to start out being checked. Like so:
<input class="cd-accordion__input" type="checkbox" name ="group-1" id="group-1" checked>
It's important to note that as you get better and better at web development (eventually learning JavaScript and a server side language such as PHP). You will be able to piece together more advanced methods to doing what we're trying to accomplish! But for now, I hope I was able to break this down for you!
I am using WooCommerce 3.5.7 , and WordPress 5.0.4.
I have a number of products attached to multiple categories:
e.g.
Product 1, attached to categories A,B, C
Product 2, attached to categories A,Y, C
etc...
I want to hide category C from the site so that it is not visible when the categories are displayed in the site category menu navigation on the front-end.
I have tried numerous approaches but none seem to work,
First Approach:
Hiding via CSS.
The structure of the navigation menu is as follows:
<li class="cat-item ">
<span class="icon-toggle"></span>
category-text
</li>
I attempted to hide the element using the below CSS:
a[href="https://siteurl.com/product-category/category-url/"]
{
display: none!important;
}
The problem with this is it removed the hyperlink and text but the category still 'took up space' on the page. This was because this only hides the anchor element and not the entire <li class="cat-item "> that is the parent of that element.
I was unable to find any way to target the parent of a child element in CSS.
Second Approach: Using pre_get_posts:
https://wordpress.stackexchange.com/questions/90923/pre-get-posts-for-exclude-category
$catid = "-1031";
$excludeCategory = function ($catid)
{
return function ($query)
{
if (
$query->is_home()
&& $query->is_main_query()
) {
$query->set('cat', $catid);
}
};
};
add_action('pre_get_posts', $excludeCategory($catid));
In the above example, the category ID I want to hide is '1031'. But this did not work.
Any suggestions, how I can remove this product category so that it does not display in the front end, but is preserved in the backend?
You could try adding onclick="hide()" to the link, then add the following javascript:
function hide() {
document.getElementsByClassName("cat-item ").style.display = "none!important";
}
I try to learn the Intel Appframework UI.
I want to build a simple offline app with just two panels.
One panel contains a list with items (links), the second panel should display details to the selected item. The details in the second panel should be filled with javascript.
How do I pass data (for example a numeric item_id) to the second panel, so that my javascript can access it and fill it into the details page?
Can I use a part of the url of the links in the list, like this:
<li>
Item 3
</li>
If yes, how do I get the information back in the details page? Or do I need to use javacript-links?
In the intel appframework forum is an answer to my question:
https://forums.html5dev-software.intel.com/viewtopic.php?f=26&t=4478
So the solution is to use urls like like this:
<li>
Item 3
</li>
We than attach a handler to the target panel, which gets the url from the browser. The framework has set the url to the new value when the callback is called:
$("#detailpanel").bind("loadpanel",function(evt){
//url would be #detailpanel/N
params = document.location.hash.split('/');
if(params.length > 1){
showDetails(params[1]);
}
else {
console.log('on_load_detailpanel: detailnr missing');
}
})
I'm not an Appframework expert, but here's the simplest solution I came up with:
Since you're on a single-page app, you can store the currently selected item in a global data structure:
<script type="text/javascript">
var dialogData = {
"currentItemNo": -1,
// possibly other dialog data
};
</script>
Then in your HTML you can set it like this:
<li>
Item 3
<li>
and access it when the user navigates to the page:
<script type="text/javascript">
$("#detailpanel").bind("loadpanel", function () {
console.log("You clicked " + dialogData.currentItemNo);
});
</script>
Note that both the click handler and AF's anchor handler get called (the click handler first).
In case your list is generated from a template you might want to use a more elegant solution to access the current item number:
<li>
Item 3
<li>
I have had a look at;
Jquery Extract URL from Text and
jquery match() variable interpolation - complex regexes
but I'm still a bit lost.
The problem;
I have a page that contains a drop down menu along the top, inside the drop down is the name of an item which I can click on to view that item. The URL of the item is unrelated to the name, it is a unique ID.
Example code of dropdown;
<div class="innerMenuWrapper">
<li class="hasSubMenu sfhover">
Items
<span class="splIcon splIcon-triangle-4-e dropDown">Submenu</span>
<div class="outerMenuWrapper splShadow" style="left: 160px; display: none; ">
<ul>
<div class="innerMenuWrapper">
<li class="">
<a href="/en-US/app/Items/itemahdwhidwbow" tabindex="-1"
class="menuItemLink">item.one</a>
</li>
<li class="">
<a href="/en-US/app/Items/itemfhfaogsgs" tabindex="-1"
class="menuItemLink">item.two</a>
</li></div></ul></div></li></div>
This is all generated externally and managed by the actual web service itself. On the page itself is a table generated from a dataset. I have generated the data with no problem and presentation is fine as-well.
What I want to do is to pull the URL from the HREF above and to wrap the same item in the table on the main page.
E.g. I have a table of 3 columns, Items, Item contents, Item price etc. I want to wrap each Item with its associated URL from the above.
So to take item.one as an example, that is the name it has in the drop down and has the URL, /en-US/app/Items/itemahdwhidwbow. Inside the data itself I have this code to select each first cell of the table and wrap the contents with a link (part of a larger code-set);
if (tr.find("td:nth-child(1)")) {
tr.find("td:nth-child(1)").wrapInner(function() {
var link = $('<a/>');
link.attr('href', 'US/app/Items/itemahdwhidwbow');
link.text($(this));
return link;
});
}
Obviously at the moment the URL is static. What I really need is a way to pull the URL from the associated link in the drop down on the same page. The name in the first table cell will always match the name in the drop down list, is there a simple way for me to do this?
What I have read so far points towards regex use but in this case I am looking for the contents of one part of the code to match something further down the page.
EDIT:
So for example, item.one has the text "item.one". "item.one" is also in the table, I want the "item.one" in the table to have the same link as in the drop down. If I could get to the text inside the anchor then I could just do a match against the text in the table
I'm not clearly understand your table layout structure but i can give you a general idea:
$('a.menuItemLink').each(function(index, linkEl){ //Iterate over dropdown links
var $link=$(linkEl);
tr.find("td:nth-child("+ index +")") //Lets say here we get required cell by index
.wrapInner(
$('<a/>').attr('href', $link.attr('href'))
);
});