I'm trying to replicate the "order summary" feature on Shopify, where all order details are hidden inside a toggle button/accordion effect to make the checkout process faster and simpler.
Bonus points if we can add a cart symbol and price to the toggle button, like in the images I've attached.
I've tried using various plugins such as Collapse-O-Matic and CSS, but have been unable to achieve the desired effect on my WooCommerce checkout page.
I'm looking for a solution to create a toggle button or accordion effect for the order summary table on the checkout page in WooCommerce.
Any help or guidance would be greatly appreciated!
Attachments:
How it should look like
Shopify order summary inside checkout
enter image descriptionwhat it looks like right now here
If you want full control you should create a custom template for the form-checkout.php template (woocommerce/templates/checkout/form-checkout.php)
But you can also add a code snippet that hides all table row of the order review, except the order total. By keeping the order total visible you don't have to worry about correctly updating your order total when values in your checkout form change. It will automatically be updated by WooCommerce.
The only other thing the snippet does is add a separate line with the cart icon and toggle buttons, some CSS and a little jQuery to hide and show the appropriate elements.
add_action( 'woocommerce_checkout_order_review', function() {
?>
<style>
#order_review_heading {
display: none;
}
#order_review_toggle {
display: inline-block;
padding: 1em;
font-size: 18px;
line-height: 1;
width: 100%;
}
#order_review_toggle .dashicons {
font-size: 18px;
}
#order_review_toggle .dashicons-cart {
margin-right: 0.5em;
}
#order_review_toggle .dashicons-arrow-up-alt2,
#order_review_toggle .dashicons-arrow-down-alt2 {
float: right;
cursor: pointer;
}
#order_review_toggle .dashicons-arrow-up-alt2 {
display: none;
}
.woocommerce-checkout-review-order-table tr:not(.order-total) {
display: none;
}
</style>
<script>
jQuery( function( $ ) {
$('#order_review_toggle .dashicons-arrow-down-alt2').on( 'click', function() {
let $orderReviewTable = $(this).closest('#order_review').find( '.woocommerce-checkout-review-order-table' );
$orderReviewTable.find('tr').show();
$(this).siblings('.dashicons-arrow-up-alt2').show();
$(this).hide();
});
$('#order_review_toggle .dashicons-arrow-up-alt2').on( 'click', function() {
let $orderReviewTable = $(this).closest('#order_review').find( '.woocommerce-checkout-review-order-table' );
$orderReviewTable.find('tr').not('.order-total').hide();
$(this).siblings('.dashicons-arrow-down-alt2').show();
$(this).hide();
});
});
</script>
<?php
printf( '<div id="order_review_toggle"><span class="dashicons dashicons-cart"></span><span>%s</span><span class="dashicons dashicons-arrow-up-alt2"></span><span class="dashicons dashicons-arrow-down-alt2"></span></div>', __( 'Your order', 'woocommerce' ) );
}, 1 );
add_action( 'wp_enqueue_scripts', function() {
if ( is_checkout() ) {
wp_enqueue_style('dashicons');
}
} );
This code snippet should be added to the functions.php of your child theme (or via a plugin like Code Snippets).
Related
ISSUE:
Everything pretty much works if I stick to layout: "fitColumns" anything else I try seems to result in rendering (if that is the correct term) issues. I am not using a framework (a bridge too far in the timeframe). When the table is displayed after the page is fully loaded, regardless of the layout I choose - it always starts off displaying as it would for "fitColumns". If I set it to "fitDataFill" for example, it loads and displays as for "fitColumns". When I click to another tab and back again
it then displays the data as it should for fitDataFill.
REVISED:
Rough order of steps:
Load a specific file which contains meta data about the rest of the files to load into tables, the names of those tables, the columns in each table and the text to display as float over help on the column headers
Load the rest of the data and build the table config object which contains the meta data
Add a table specific div for each table and append to the csv-tab-buttons div
Build the table on the newly created div
Add the button which will toggle which table gets display via the css active class and the setTab function
The setTab function includes two redraws (to be sure, to be sure).
If you see code that resembles something you wrote here then thanks to you, much of what I wrote I gleaned from others.
Tabulator version is 4.8
Code is created using Visual Studio Code using the Live Server extension to reload the page after every save
Browser is Chrome Version 85.0.4183.121 (Official Build) (64-bit)
I rewrote based on the suggestions of #mirza to ensure the data is read in entirely before the tables are built and each table is stored independently of the others so I don't pass the same table to setTab every time
fitColumns works but does not allow me to resize columns individually (which may be by design)
fitData seems to work ok
fitDataFill and fitDataStretch do not work on the first render, or subsequent renders of the same table until I click away and back again
I have attempted to follow the logic in the tabulator.js via the debugger and although I can see what is happening, there is way too much going on in there for me to grasp where the issue might be
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<!-- CSS -->
<link rel="stylesheet" href="./scripts/dist/css/tabulator.min.css">
<link rel="stylesheet" href="./styles/style.css">
<!-- Scripts -->
<script type="text/javascript" src="./scripts/dist/js/tabulator.min.js"></script>
<script type="text/javascript" src="./scripts/dist/js/papaparse.min.js"></script>
<title>My Team</title>
</head>
<body>
<!-- Wrapper -->
<div class="wrapper">
<section class="container">
<!-- Header -->
<header id="header" class="header">
<h1>Reporting</h1>
</header>
<!-- Tabs -->
<div id="csv-tab-buttons" class="tab">
</div>
<!-- Tables on each tab -->
<div id="csv-tabs">
</div>
<!-- Footer -->
<footer class="footer">
<p>My Team © 2020</p>
</footer>
</section>
</div><!-- Wrapper Ends-->
<script src="./scripts/dist/js/miscsv.min.js"></script>
</body>
</html>
REVISED Javascript:
//*******************************************************************************************************
// Global variables
//*******************************************************************************************************
var file = 'DS.PPTE.DB2.VIARACF.VARLEGND.CSV'
var tables = []
var tableDivs = []
var tabConfig = {}
//*******************************************************************************************************
// Global functions
//*******************************************************************************************************
let onlyUnique = (value, index, self) => {
return self.indexOf(value) === index
}
//*******************************************************************************************************
// Async functions
//*******************************************************************************************************
// Set the tab to whichever button was clicked
async function activateTab(target) {
// hides all tabs
document.querySelectorAll(".tabcontent").forEach(tabContent => tabContent.style.display = "none");
// Remove the active class from all tab links
document.querySelectorAll('.tablinks').forEach(tabLink => tabLink.className.replace("active", ""));
// Remove the active class from the active tab
document.querySelectorAll(".active").forEach(activeTab => activeTab.classList.remove("active"))
// Activate the selected tab
document.querySelector(`#${target.textContent}`).style.display = "block"
target.classList.add("active");
}
async function setTab(target) {
console.log("Activate the tab")
await activateTab(target)
console.log("Redraw the table")
// Redraw the table
tableDivs[`${target.textContent}`].redraw(true);
}
// Read a CSV file
const readCSV = async (file) => {
return new Promise(resolve => {
Papa.parse(`data/${file}`, {
header: true,
download: true,
skipEmptyLines: true,
complete: results => {
console.log(`${file} loaded - ${results.data.length} records.`)
resolve(results)
}
})
})
}
// Get all the data first
async function getData() {
// Read the meta data file with the data and table config
let parseMeta = await readCSV(file)
tabConfig = {
// Get the names of the tables to present
tabs: parseMeta.data.map((data) => data['TABLE-NAME']).filter(onlyUnique).sort(),
// Find the file name for each table
files: parseMeta.data.map((data) => `${data['TABLE-NAME']}-${data['CSV-FILE-NAME']}`).filter(onlyUnique)
.map((entry) => {
let tmpEntry = entry.split('-')
return { table: `${tmpEntry[0]}`, file: `${tmpEntry[1]}` }
}),
// Save the float over help for each column by table name
help: parseMeta.data.map((data) => {
return { key: `${data['TABLE-NAME']}-${data['VARIABLE']}`, helpText: data['VAR-DESCRIPTION'] != '' ? data['VAR-DESCRIPTION'] : data['VARIABLE'] }
}),
data: tables,
divs: tableDivs,
}
// Read in the files which contain the table data
for (const tabName of tabConfig.tabs) {
let file = tabConfig.files.filter(entry => entry.table == tabName)[0].file
tables[tabName] = await readCSV(file)
tableDivs[tabName] = `csv-table-${tabName}`
}
}
// Master function to do everything in the right order
async function doAll() {
// Get all the data and build the table config
await getData()
// Store the buttons and tabs anchor divs
let buttonsDiv = document.getElementById("csv-tab-buttons")
let tabsDiv = document.getElementById("csv-tabs")
// Add the buttons and tables
for ([idx, tabName] of tabConfig.tabs.entries()) {
// Add tabs to hold the tables to the page
const elemTabDiv = document.createElement('div')
const elemTableDiv = document.createElement('div')
elemTabDiv.id = tabName
elemTabDiv.className = "tabcontent"
elemTableDiv.id = `csv-table-${tabName}`
elemTableDiv.className = "table"
elemTabDiv.appendChild(elemTableDiv)
tabsDiv.appendChild(elemTabDiv)
// Define header context menu
let headerMenu = [
{
label:"Hide Column",
action:function(e, column){
column.hide()
},
},
]
// Create the table
tableDivs[tabName] = new Tabulator(`#csv-table-${tabName}`, {
data:tabConfig.data[tabName].data,
layout:"fitData",
responsiveLayout:"collapse",
tooltips:true,
pagination:"local",
paginationSize:20,
resizableColumns:true,
movableColumns:true,
resizableRows:true,
autoColumns: true,
autoColumnsDefinitions: function(definitions) {
definitions.forEach((column) => {
let helpText = tabConfig.help.find(key => key.key === `${tabName}-${column.field}`).help
// Add float over help based on column name
column.headerTooltip = helpText
column.headerMenu = headerMenu
column.headerFilter = true
column.headerSort = true
column.headerFilterLiveFilter = false
})
return definitions
},
renderStarted:function(){
console.log("Render started")
},
renderComplete:function(){
console.log("Render complete")
},
})
// Add tab buttons to page
const elemTabButton = document.createElement('button')
elemTabButton.id = `button-${tabName}`
if ( idx == 0 ) {
elemTabButton.className = "tablinks active"
} else {
elemTabButton.className = "tablinks"
}
elemTabButton.onclick = function() { setTab(this) }
elemTabButton.textContent = tabName
buttonsDiv.appendChild(elemTabButton)
}
document.querySelector(".active").click();
}
doAll()
CSS:
:root {
--shadow: 0 1px 5px rgba(104, 104, 104, 0.8);
--raisin: #262730;
--vermillion: #d33f49;
--cadet: #576c75;
--navyboy: #8db2c2;
--space-cadet: #363457;
--baby-powder: #f0f4ef;
--ice: rgb(245, 247, 253);
}
html {
box-sizing: border-box;
font-family: Arial, Helvetica, sans-serif;
color: var(--dark);
}
body {
background: var(--baby-powder);
margin: 10px 10px;
line-height: 1.4;
}
/* .wrapper {
display: grid;
grid-gap: 10px;
} */
.container {
display: grid;
grid-gap: 10px;
grid-template-areas:
'header'
'csv-tab-buttons'
'footer';
margin: auto;
width: 98%;
overflow: auto;
padding: 1rem 1rem;
}
header {
background: var(--raisin);
color: var(--vermillion);
font-size: 150%;
line-height: 1;
padding: 0.1rem;
text-align: center;
box-shadow: var(--shadow);
}
.tab {
background-color: var(--ice);
box-shadow: var(--shadow);
}
/* Style the buttons that are used to open the tab content */
.tab button {
background-color: inherit;
/* float: left; */
border: none;
outline: none;
cursor: pointer;
/* padding: 14px 16px; */
padding: 1rem 1.1rem;
transition: 0.3s;
}
/* Change background color of buttons on hover */
.tab button:hover {
background-color: var(--cadet);
color: var(--ice);
}
/* Create an active/current tablink class */
.tab button.active {
background-color: var(--vermillion);
color: var(--ice);
}
/* Style the tab content */
.tabcontent {
/* overflow: hidden; */
display: none;
/* padding: 6px 12px; */
/* border-top: none; */
}
/* Override Tabulator header background */
.tabulator-col-content {
background-color: var(--ice);
}
.table {
overflow: hidden;
box-shadow: var(--shadow);
}
.footer {
background: var(--cadet);
color: white;
padding: 0rem 2rem;
line-height: 1;
box-shadow: var(--shadow);
}
EDIT:
I made some changes to the order of everything so I could keep the table object in scope when the setTab function is called and I could issue the redraw when the tab button is clicked. The first time I click through every tab it appears to be filling to the width of the data. When I click through each tab again it is properly wrapping columns that would be off screen to the next line. I put multiple table redraws in the setTab routine and it makes no difference to the rendering of the table. It does however change the attributes of the table in some way. I observed in the debugger that tableWidth changed from 0 prior to the first redraw to 2734, the to 2786 after the second redraw and it stayed at the value. If I click away and back again it wraps as expected.
Tabulator table does not properly render if the element is not visible when creating the table.
To properly render the table, you have to redraw the table when the element is visible.
See Here
From the website:
If the size of the element containing the Tabulator changes (and you are not able to use the in built auto-resize functionality) or you create a table before its containing element is visible, it will necessary to redraw the table to make sure the rows and columns render correctly.
You can redraw the table by
table.redraw();
Or
table.redraw(true); //trigger full rerender including all data and rows
http://boxes.site44.com/
I have these boxes, and when a user clicks book in any of them, the clicked box will expand and will appear a form.
Now i am trying to populate the dropdown on the left upper side (you have to click on book to see) with the h2 tags (which box have the h2 title tag) and change the content, depending on the h2 clicked by the user.
The HTML is very large, because of that i made a fiddle:
Fiddle: http://jsfiddle.net/62Lw4bjz/
The to expand the box is:
var $expander = $('.event .cta');
var $actual = $();
var $others = $();
var $clicked = $();
$expander.click(function(e) {
e.preventDefault();
$clicked = $(this);
$clicked.addClass('hidden');
$actual = $(this).closest('.event');
$others = $('.event').not($actual);
$others.addClass('hidden');
$actual.find('.event__info-list').addClass('inline');
$actual.find('h2')
.addClass('event__close')
.append('<span class="event__arrow" aria-hidden="true">▼</span>');
$actual.find('.expanded').slideDown().css('display', 'flex');
$("html").velocity("scroll", { offset: $("#sell").offset().top, mobileHA: false });
$actual.velocity('transition.expandForm', {
delay: 0,
duration: 800,
ease: 'easeOutSine'
});
});
All the dropdowns that i saw, works with lists (<li>), it is possible populate the dropdown with the h2s title tags from each box, or i need change the html in order to make it work?
How to change the box based on the clicked h2?
Is there example code to help me with this?
Should I use jquery's change to achieve this?
I am open to suggestions, i am stuck on this for 3 days..
Thanks very much.
Update:
This is not a dropdown when the box expand, but i need to bee a dropdown that has options to navigate to the other boxes.
http://jsfiddle.net/62Lw4bjz/4/
Edited your jQuery find in your $expander.click function. This will append a list to the h2 element. Hide it using CSS and displays/hides it when the user clicks on the menu. In a very basic way this will do it:
$actual.find('h2')
.addClass('event__close')
.append('<span class="event__arrow" aria-hidden="true">▼</span>')
.append('<ul class="dropdown hidden"><li>test</li><li>test</li><li>test</li></ul>')
.click(function(){
if ($(this).attr("data-expanded") == true)
{
$(this).find("ul").toggleClass('hidden');
$(this).attr("data-expanded", false);
}
else
{
$(this).find("ul").toggleClass('hidden');
$(this).attr("data-expanded", true);
}
});
CSS:
ul.dropdown {
display: block;
position:absolute;
background-color: #ffffff;
box-shadow: 2px 2px 2px 2px rgba(150, 150, 150, 0.6);
z-index: 1000;
list-style-type: none;
width: 200px;
padding: 10px;
}
ul.dropdown.hidden {
display: none;
position:absolute;
}
ul.dropdown > li:hover {
background-color: #FFCC99;
}
jQuery append also accepts a function as input. So you can dynamically build the HTML string with other book options.
What you need to do yourself:
Attach click handlers to the list items.
Change the list items to the box references you want them to be.
Handle the hiding of the menu when your box is collapsed.
I need to a a following button like this example
What i should write in java script?
$('.following').hover(function(){
$(this).text("Unfollow");
},function(){
$(this).text("Following");
});
//for toggle the class following/follow When click
$('.following').click(function(){
$(this).toggleClass('following follow').unbind("hover");
if($(this).is('.follow')){
$(this).text("Follow");
}
else{
//binding mouse hover functionality
$(this).bind({
mouseleave:function(){$(this).text("Following");},
mouseenter:function(){$(this).text("Unfollow");}
});
}
});
what changes need to have bootstap button?
You can do this in CSS like so: http://jsfiddle.net/Th4th/
.follow {
width: 100px;
height: 40px;
background-color: gray;
}
.follow:hover span {display:none;}
.follow:hover:before {
content: "Unfollow"
}
I don't think you'll need java script for the button changing, only for taking the action of the click.
I'm building a local website on the Bitnami Wordpress Stack with the Arras theme, if that's important.
I'm making a fixed menu that I want to show after I have scrolled 190 pixels down on the page. The problem is that anything works, no matter which JQuery or JavaScript code I try. I have searched and searched here on StackOverflow, and I know that this question have been asked numerous times here before - but I have tried every code I could find, and none works. This is my JavaScript/JQuery/HTML/PHP code for my menu, placed in the header.php file:
<div class="medfolg" id="medfolg">
<script type="text/javascript">
$(document).ready(function(){
$(window).bind('scroll', function(){
if($(window).scrollTop() > 190){
$('#medfolg').show();
} else {
$('#medfolg').hide();
};
});
});
</script>
<?php
if ( function_exists('wp_nav_menu') ) {
wp_nav_menu( array(
'menu' => 'medfolg',
'menu_class' => 'sf-menu'
) );
}
?>
</div>
And this is the CSS code I have placed in my default.css file:
#medfolg.medfolg {position:fixed;}
#medfolg { text-transform: lowercase; position: absolute; top: 0; width: 100%; background: #f5f5f5; z-index:5000; display: none;}
#medfolg .menu-medfolg-container { width: 980px; margin: 0 auto; }
#medfolg .sf-menu { position: relative; top:3px !important; }
#medfolg .sf-menu a { font-size: 22px; color: #444; margin-right: 15px;}
I desperately need some help - please!
EDIT: I've made a jsFiddle here with only small modifications (Wordpress .php menu cannot be read on other places than Wordpress): http://jsfiddle.net/wHMjr/
For completeness, I will post my code:
First, the code is wrapped with a self-executing function to prevent conflict between libraries:
(function($){
//code goes here, now $ is a local reference to the jQuery object.
})(jQuery)
Then, I create the handler:
var setMenuVisibility = function(){
if($(window).scrollTop() > 190){
$('#medfolg').show();
} else {
$('#medfolg').hide();
};
}
which will be attached to the window's scroll event and change the menu's visibility according to the scroll status.
The attachment is done by binding the function to the event:
$(window).bind('scroll', setMenuVisibility);
//and set the initial visibility
setMenuVisibility();
The last line in the above section sets the initial state of the menu, as it is possible that the initial scroll value would require it to be visible (e.g, a link to a lower subsection of the page).
The entire process is initiated when the document's markup is ready.
See demo here.
Try using vanilla js instaead of $(window).scrollTop() try window.scrollY > 190
for the complete function I would use toggle too so:
jQuery
//Use jQuery to make sure we are using correct function
jQuery(window).on('scroll', function () {
var el = jQuery('#medfolg');
if (window.scrollY > 100) {
el.show(); //Use this to toggle element visibility
} else {
el.hide()
}
});
HTML
<div style="display:none" id="medfolg">
<ul>
<li>Home
</li>
<li>Tutorials
</li>
<li>Articles
</li>
<li>Inspiration
</li>
</ul>
</div>
Demo
I've made a script which adds an image and a textbox to a canvas. In order for the textbox to appear over the image, I need to apply CSS to the image. Therefore I made to separate divs, <img> for the image and <text> for the text.
This creates two problems. When I fist add the image and the text the two elements are considered separate and I can only drag either the image or the text, rather than the two elements at the same time. Furthermore, when I add another image and another textbox the two don't appear on top of each other, rather the text is spawned next to the textbox and the image next to the image.
So my question is this. Is it possible to consolidate these two elements into one and when spawned make them do so, make them do it the same way every time?
Here is my Javascript I made for it.
$(document).ready(function() {
$("#img").draggable();
$("#text").draggable();
});
function addEvent() {
$("#img").append("<img src='Sticky_Note.png'</a>")
$("#text").append(" <textarea name='comments' x-webkit-speech='x-webkit-speech'style='width:380px;height:300px; background: none; border:none; font-size:24px; color:#0033FF;font-family:comic sans ms' />")
}
Should this not be enough information (I'm n00btastic, sorry), I 've attactched a bleeding edge link here.
Thanks for your help!
Try creating a wrapper <div> around your image and text elements, e.g. <div id="#wrapper">. Then you can do the following:
$('#wrapper').draggable();
Your addEvent code would look like:
function addEvent() {
$("#wrapper").append("<img src='Sticky_Note.png'</a>");
$("#wrapper").append(" <textarea name='comments' x-webkit-speech='x-webkit-speech'style='width:380px;height:300px; background: none; border:none; font-size:24px; color:#0033FF;font-family:comic sans ms' />");
}
there's a few issues with your implementation, I'll ignore the basic document issues as I figure you were putting together a quick example :)
When you append a new 'note' it's not individually movable, it's the container that is movable so they just stack up as you're only adding more content to the corkboard container, ie: another textarea/image, then another textarea/image.
I've put up a solution on pastebin here, feel free to have a look, hopefully it helps point you in the right direction.
Actually, i'll embed it too, for posterity
<!DOCTYPE html>
<html>
<head>
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" rel="stylesheet" type="text/css"/>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>
<style>
#corkboard { position: relative; }
#corkboard textarea { position: absolute; width: 380px; height: 300px; top: 100px; left: 60px; background: none; border: none; font-size: 24px; color: #03f; font-family: 'comic sans ms', serif; }
#corkboard img { position: absolute; }
.corkboard-item {
position: absolute;
float: left;
width: 500px;
height: 500px;
}
</style>
<script>
$( document ).ready( function() {
// Create our corkboard item
//
$corkboard_item = $( '<div></div>' );
$corkboard_item
.addClass( 'corkboard-item' )
.append( '<img src="http://www.bookofsam.com/corkboard/Sticky_Note.png" />' )
.append( '<textarea name="comments" x-webkit-speech="x-webkit-speech">Enter your note</textarea>' );
// Counter goodness
$x = 1;
// Make sure the one we click is always at the top
//
$( 'img, textarea' ).live( 'click', function( e ) {
e.preventDefault();
$( '.corkboard-item' ).each( function() {
$( this ).css({ 'z-index':1 });
});
$( this )
.parent()
.css({
'z-index':2
})
});
// Add one when we click the button
//
$( 'a.add-item' ).live( 'click', function( e ) {
e.preventDefault();
var item = $corkboard_item.clone();
$( item )
.find( 'textarea' )
.attr({
'name' : ( 'comment-' + $x )
});
$x++;
$("#corkboard").append( $( item ).draggable() );
});
});
</script>
</head>
<body>
<p>CORKBOARD TEST - 0.8.555 </p>
<img src='http://www.bookofsam.com/corkboard/Note_Scale.png' align="right" />
<div id="corkboard"></div>
</html>
Thinking about it, I should've really put the 'note' image as a background image on the 'corkboard-item'.
:)
nbsp;