How can I test the Material-UI tooltip with cypress? - material-ui

We use tooltips a lot and I wanted to use the tooltip text to find elements. This works fine the first time, as the tooltip text is just in the title in the element. But on mouse over the title proprerty is removed and a new tooltip element is added at the bottom of the page. After issueing a click on the element, the mouse stays over it and the next time I can't find it by the title.
Without mouse over:
<span class="" title="Save changes">
...
</span>
with mouse over:
<span class="" aria-describedby="mui-97465">
...
</span>
...
<div role="tooltip" class="MuiTooltip-popper" id="mui-97465" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(3063px, 116px, 0px);" x-placement="bottom">
<div class="MuiTooltip-tooltip MuiTooltip-tooltipPlacementBottom" style="opacity: 1; transform: none; transition: opacity 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, transform 133ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;">
Save changes
</div>
</div>
I tried building my own command, but that doesn't work either:
Cypress.Commands.add("findByTooltip", (text) => {
cy.get("body")
.then(($body) => {
$body.find(`[title="${text}"]`).length > 0;
})
.then((byTitle) => {
byTitle
? cy.findByTitle(text)
: cy
.findByRole("tooltip")
.contains(text)
.its("id")
.then((id) => cy.get(`[aria-describedby=${id}]`));
});
});
Has anyone done something like this?

This part is missing a return value
.then(($body) => {
$body.find(`[title="${text}"]`).length > 0;
})
so either
.then(($body) => {
return $body.find(`[title="${text}"]`).length > 0;
})
or remove the brackets for "implied return"
.then(($body) => $body.find(`[title="${text}"]`).length > 0 )
I'm not sure title="Save Changes" is on the page DOM, at least the one MUI example is missing that attribute.
I would go for the aria-label
.then(($body) => $body.find(`[aria-label="${text}"]`).length > 0 )
To make the tooltip show, you need a hover of some kind. The most reliable seems to be cypress-real-events
cy.get((`[aria-label="${text}"]`)).realHover();
cy.findByRole("tooltip")
.contains(text)
.its("id")
.then((id) => cy.get(`[aria-describedby=${id}]`))

Related

Antd Modal+Carousel+Form shudders for 10ms

During opening antd modal I can see for 10ms that my modal is increased and displays not the first page of the carousel inside.
Here I have attached a Json file, which you can play in your Chrome DevTools (F12 -> Performance -> Load profile). Described problem appears on 1150ms. Here is the link to the codesanbox where you can play with my code. I found that the modal increases because my form layout equal to "vertical", but I still didn't get why the last page of my carousel appears first. I am thinking of some hooks in React like useLayoutEffect to render it correctly, however it seems to my as a antd bug, or my mistake somewhere and I don't want to overload my code with useless hooks.
Here is the code for that who won't open codesanbox:
import "./styles.css";
import React, { useState, useRef } from "react";
import { Modal, Carousel, Form, Button, Input, Image } from "antd";
export default function App() {
const [modal, setModal] = useState<boolean>(false);
const [pageOneForm] = Form.useForm();
const [pageTwoForm] = Form.useForm();
return (
<>
<Button onClick={() => setModal(true)}> open </Button>
<Modal
width={500}
centered
closable={false}
title={"Title of my modal"}
open={modal}
onCancel={() => setModal(false)}
okButtonProps={{ style: { display: "none" } }}
>
<Carousel dotPosition="top" swipeToSlide draggable arrows>
<div>
<img alt="card1" src="card1.png" className="card" />
<Form
form={pageOneForm}
layout="vertical" //it makes my modal huge for few ms
>
<Form.Item
label="Some input for 1 page"
name="carteVitale"
rules={[{ required: true, message: "" }]}
>
<Input />
</Form.Item>
</Form>
</div>
<div>
<img alt="card2" src="card2.png" className="card" />
<Form form={pageTwoForm} layout="vertical">
<Form.Item
label="Some input for 2 page"
rules={[{ required: true, message: "" }]}
>
<Input />
</Form.Item>
</Form>
</div>
</Carousel>
</Modal>
</>
);
}
I expect to render this modal without unplanned modal shakes
UPDATE:
Size of modal can be fixed by adding two keys to Carousel component:
<Carousel
adaptiveHeight={false}
lazyLoad="progressive"
...
or by playing with css:
<Carousel
initialSlide={0}
lazyLoad="ondemand"
className="cards-container"
...
<Form.Item
className="no-wrap"
...
and css will looks like:
.cards-container {
animation: fadein 0.5s ease;
position: relative;
}
#keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.no-wrap label {
white-space: nowrap;
overflow: hidden;
}

WebdriverIO can't click on (Ionic) tab

I'm using WebdriverIO to test some basic functionality on an Ionic (+ Angular) application. The framework setup works all right, but I can't find a way to click on certain HTML elements. For instance, this is some HTML sample:
<div class="tabbar show-tabbar" role="tablist" style="top: 166px; display: flex;">
<a class="tab-button has-title has-title-only" href="#" role="tab" id="tab-t0-0" aria-controls="tabpanel-t0-0"
aria-selected="true">
<span class="tab-button-text">Blah</span>
<div class="button-effect"></div>
</a>
<a class="tab-button has-title has-title-only" href="#" role="tab" id="tab-t0-1" aria-controls="tabpanel-t0-1"
aria-selected="false">
<span class="tab-button-text">Foo</span>
<div class="button-effect"
style="transform: translate3d(83px, -103px, 0px) scale(1); height: 240px; width: 240px; opacity: 0; transition: transform 395ms ease 0s, opacity 277ms ease 118ms;"></div>
</a>
<a class="tab-button has-title has-title-only" href="#" role="tab" id="tab-t0-2" aria-controls="tabpanel-t0-2"
aria-selected="false">
<span class="tab-button-text">Bar</span>
<div class="button-effect"
style="transform: translate3d(3px, -99px, 0px) scale(1); height: 240px; width: 240px; opacity: 0; transition: transform 395ms ease 0s, opacity 277ms ease 118ms;"></div>
</a>
<div class="tab-highlight animate" style="transform: translate3d(570px, 0px, 0px) scaleX(285);"></div>
</div>
And this is a super-simple test case that I'm doing to test the functionality:
import { expect } from "chai";
describe("Some Test", () => {
const logingUrl: string = "url0";
const appUrl: string = "url1";
it("Some Test Again", () => {
browser.url(logingUrl);
browser.url(appUrl);
const tab = $("#tab-t0-2");
tab.click();
expect(tab.getAttribute("aria-selected")).to.equal("true");
});
});
..but every time I run it, I get some weird error message that the element is no clickable at some point. Any clues?
[0-0] element click intercepted in "Some Test Again"
element click intercepted: Element <a class="tab-button has-title has-title-only" href="#" role="tab" id="tab-t0-2" aria-controls="tabpanel-t0-2" aria-selected="false">...</a> is not clickable at point (666, 170). Other element would receive the click: <ion-backdrop disable-activated="" role="presentation" tappable="" class="backdrop-no-tappable" style="opacity: 0.5;"></ion-backdrop>
(Session info: headless chrome=75.0.3770.100)
This is one of those classic Angular/Ionic backdrop gotcha's.
Let's start with the error message: element #tab-t0-2 is not clickable at point (coordinates), another element would receive the click: ion-backdrop.
This tells us that your targeted element cannot be clicked as the ion-backdrop tag is rendered on top of it. The ion-backdrop component is a short animation (usually used for modals), in this case, the semi dimming of the background (opacity: 0.5).
✖ Solution 1: Easy way to counter it? Explicitly expect for it to disappear, then click the targeted element.
it("Some Test Again", () => {
browser.url(logingUrl);
browser.url(appUrl);
// Explicitly wait for the backdrop animation to disappear:
const backdrop = $('.backdrop-no-tappable');
backdrop.waitForExist(5000, true, 'Backdrop still present');
const tab = $("#tab-t0-2");
tab.click();
expect(tab.getAttribute("aria-selected")).to.equal("true");
});
✖ Solution 2: Another thing you can try is only click on the tab element, once it is fully visible and intractable in the DOM (this is kind of a best-practice):
const tab = $("#tab-t0-2");
tab.waitForDisplayed(5000);
// For 'wdio-v4' users:
// tab.waitForVisible(5000);
tab.click();
expect(tab.getAttribute("aria-selected")).to.equal("true");

Combining Swiper slider and photoswipe

I'm looking for a combination of Swiper slider and Photoswipe (Or other lightbox).
Trying to make a product slider with 3 products in a slide.
Each product has a lightbox/modal with video and a gallery.
The modals are generated within the boundaries of the product div.
When you click an 'open gallery' / 'show video' link. The lightbox opens fullscreen.
The problem I'm having is: the lightbox won't (but has to) exceed the boundary of the slider product boundary.
Looking for a solution.
Something like an empty modal/lightbox containers outside the slider with dynamic content when an 'open modal' link is clicked within the product slide.
You can check it, here is example:
<header>
<h1>
<a title="swiper.js" href="http://idangero.us/swiper/" target="_blank">Swiper.js (5.3.7)</a> &
<a title="photoswipe" href="http://photoswipe.com/" target="_blank">Photoswipe.js (4.1.3)</a> - Mobile Native feel
slider gallery
</h1>
<p>Combine two of the most powerfull JS plugins (Endless options / Great docs / Fast / Modern / Mobile freindly) -
<a title="swiper.js" href="http://idangero.us/swiper/" target="_blank">SWIPER</a> IS PERFECT FOR THIS IDEA BEACUSE OF
ITS unique <code>preventClicks</code> Parameter (Prevent accidental unwanted clicks on links during swiping) -
<strong>Works like magic</strong>. Also its really <b>hard</b> to find - Code example of working photoswipe
combination with any slider out there(slick, flickity, owl etc.) and
in general slider & lightbox - so i hope this example be usefull for you.</p>
</header>
<!-- https://swiperjs.com/get-started/ -->
<!-- Slider main container -->
<div class="swiper-container">
<!-- Additional required wrapper -->
<ul class="swiper-wrapper my-gallery" itemscope itemtype="http://schema.org/ImageGallery">
<!-- Slides -->
<li id="1" class="swiper-slide" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
<a id="first" title="click to zoom-in" href="https://picsum.photos/id/1079/1200/600" itemprop="contentUrl" data-size="1200x600">
<img src="https://picsum.photos/id/1079/1200/600" itemprop="thumbnail" alt="Image description" />
</a>
</li>
<li id="2" class="swiper-slide" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
<a title="click to zoom-in" href="http://placehold.it/1200x601/AB47BC/ffffff?text=Zoom-image-2"
itemprop="contentUrl" data-size="1200x601">
<img src="http://placehold.it/600x300/AB47BC/ffffff?text=Thumbnail-image-2" itemprop="thumbnail" alt="Image description" />
</a>
</li>
<li id="3" class="swiper-slide" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
<a title="click to zoom-in" href="http://placehold.it/1200x600/EF5350/ffffff?text=Zoom-image-3" itemprop="contentUrl" data-size="1200x600">
<img src="http://placehold.it/600x300/EF5350/ffffff?text=Thumbnail-image-3" itemprop="thumbnail" alt="Image description" />
</a>
</li>
<li id="4" class="swiper-slide" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
<a title="click to zoom-in" href="http://placehold.it/1200x600/1976D2/ffffff?text=Zoom-image-4" itemprop="contentUrl" data-size="1200x600">
<img src="http://placehold.it/600x300/1976D2/ffffff?text=Thumbnail-image-4" itemprop="thumbnail" alt="Image description" />
</a>
</li>
<li id="5" class="swiper-slide" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
<a title="click to zoom-in" href="https://picsum.photos/id/1011/1200/600" itemprop="contentUrl"
data-size="1200x600">
<img src="https://picsum.photos/id/1011/1200/600" itemprop="thumbnail" alt="Image description" />
</a>
</li>
</ul>
<!-- Add Pagination -->
<div class="swiper-pagination"></div>
<!-- If we need navigation buttons -->
<div title="Prev" class="swiper-button-prev"></div>
<div title="Next" class="swiper-button-next"></div>
</div>
<!-- https://photoswipe.com/documentation/getting-started.html -->
<!-- add PhotoSwipe (.pswp) element to DOM -
Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
<!-- Background of PhotoSwipe.
It's a separate element, as animating opacity is faster than rgba(). -->
<div class="pswp__bg"></div>
<!-- Slides wrapper with overflow:hidden. -->
<div class="pswp__scroll-wrap">
<!-- Container that holds slides. PhotoSwipe keeps only 3 slides in DOM to save memory. -->
<!-- don't modify these 3 pswp__item elements, data is added later on. -->
<div class="pswp__container">
<div class="pswp__item"></div>
<div class="pswp__item"></div>
<div class="pswp__item"></div>
</div>
<!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
<div class="pswp__ui pswp__ui--hidden">
<div class="pswp__top-bar">
<!-- Controls are self-explanatory. Order can be changed. -->
<div class="pswp__counter"></div>
<button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
<button class="pswp__button pswp__button--share" title="Share"></button>
<button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
<button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
<!-- Preloader demo https://codepen.io/dimsemenov/pen/yyBWoR -->
<!-- element will get class pswp__preloader--active when preloader is running -->
<div class="pswp__preloader">
<div class="pswp__preloader__icn">
<div class="pswp__preloader__cut">
<div class="pswp__preloader__donut"></div>
</div>
</div>
</div>
</div>
<div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
<div class="pswp__share-tooltip"></div>
</div>
<button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
</button>
<button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
</button>
<div class="pswp__caption">
<div class="pswp__caption__center"></div>
</div>
</div>
</div>
</div>
<!-- ////////////////////////
DOCS
////////////////////////////
-->
<!-- Include Tippy -->
<script src="https://unpkg.com/tippy.js#3/dist/tippy.all.min.js"></script>
<!-- OPTIONAL: Set the defaults for the auto-initialized tooltips -->
<script>
tippy('.swiper-button-prev', {
content: "Prev",
theme: "light",
arrow: true,
})
tippy('.swiper-button-next', {
content: "Next",
theme: "light",
arrow: true,
})
</script>
<section id="docs">
<br>
<br>
<br>
<hr>
<h2>Noted / Important</h2>
<ol>
<li>
<h3>
A non-jQuery dependent
</h3>
</li>
<li>
<h3>Cdns</h3>
<h4>Head (CSS)</h4>
<code>
<!-- photoswipe CSS -->
<br>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe.min.css" />
<br>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/default-skin/default-skin.min.css" />
<br>
<!-- swiper CSS -->
<br>
<link rel="stylesheet" href="https://unpkg.com/swiper/css/swiper.min.css" />
</code>
<h4>Before body (JS)</h4>
<code>
<script src="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe.min.js"></script>
<br>
<script src="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe-ui-default.min.js"></script>
<br>
<script src="https://unpkg.com/swiper/js/swiper.min.js"></script>
<br>
<!-- copy-paste the code under codepen js tab her (wrap with script tag) -->
</code>
</li>
<li>
<h3>Loop & Counter</h3>
<p>
<strong> Wont work well </strong> with swiper: <code>loop = true;</code> & photoswipe: <code>counterEl:
true,</code>(What is counter? example: 1/5...2/5) - "loop" duplicate images - the photoswipe counter will be
wrong. *** If you dont want a
loop - you can set photoswipe counter <code>counterEl: true,</code>
</p>
</li>
<li>
<h3><a target="_blank" href="https://schema.org/ImageGallery">Schema.org - ImageGallery</a> Markup (0 ERRORS 0 WARNINGS )</h3>
<p>
Schema.org markup + semantic HTML: use unordered (bulleted) list (If you want a <code>div</code> under photoswipe - change JS -
<strong>"(find) control+f-->"</strong> tagname value) . Copy-paste - this code to check: <a target="_blank"
href="https://search.google.com/structured-data/testing-tool">Structured Data Testing Tool - Google</a>
</p>
</li>
<li>
<h3>Match index - BY API</h3>
<p>
<strong>
Extra CODE "match index"
</strong> - EXAMPLE: When you click(zoom) image1 -- goes to image 2 - close image2 (X) - also the swiper update
is position (<strong>BETTER</strong> User Experience) (find<kbd>(ctr +f)</kbd>-->
<code>mySwiper.slideTo(getCurrentIndex, false);</code>) -
This idea miss
in most slider & lightbox examples/plugins mixed.
<br>
Very simple code idea (100% API solution) - get photoswipe index (for example 2) and swiper slideTo index (2 - in this example).
<ul>
<li >
<a target="_blank" href="https://photoswipe.com/documentation/api.html">Photoswipe API - <strong>pswp.getCurrentIndex()</strong></a>
<li style="border-top-width: 0;"> <a target="_blank" href="https://swiperjs.com/api/#methods">Swiper API - <strong>slideTo(index);</strong></a>
</li>
</li>
</ul>
</p>
</li>
<li>
<h3>Photoswipe options</h3>
<p>
JS - line (find) -ctr +f --> the term:<code>// define options (if needed)</code>. You find endless options for
<strong>photoswipe</strong> - This is the place to add/modify options. Full Options list her
<a href="http://photoswipe.com/documentation/options.html" target="_blank">PhotoSwipe
Options</a>
</p>
</li>
<li>
<h3>SWIPER options</h3>
<h4>slideperview</h4>
<p>
<code>slideperview</code> - option1: Set number (1,2,3 and so on) - example |||||
option2(<b>"Carousel Mode"</b> this example): Set to "<code>auto</code>"
than add CSS <a href="https://www.w3schools.com/cssref/pr_dim_width.asp" target="_blank">width
Property</a></code> <code>.swiper-slide</code> (in thie case eash slide is 88% width) - example.
</p>
<h4>spaceBetween & centeredSlides</h4>
<p>
Space Between slide by js option <code>spaceBetween</code> - and also usefull to change
<code>centeredSlides</code>(true/flase). <br>
Swiper API
</p>
</li>
</ol>
<hr>
<h3>Related Example</h3>
<p>
<a title="FancyBox3 & Flickity" href="https://codepen.io/ezra_siton/pen/OQmjoq" target="_blank">#FancyBox3 -
lightbox & Flickity Slider</a>
</p>
</section>
/* zero custom styles for photoswipe */
/*==================================
SWIPER - minimal styling
===================================*/
/* semantic HTML - remove bullet and space from the list */
ul.swiper-wrapper {
list-style-type: none;
margin: 0;
padding: 0;
}
/* Swiper container */
.swiper-container {
max-width: 100%;
}
/* swiper responive image */
.swiper-container img {
width: 100%;
height: auto;
}
.swiper-slide {
/* Remove this if you want 1 slide perview - than change slidesPerView js-option to 1 -or- 2+ instead of 'auto' */
width: 80%;
}
/* Swiper custom pagination */
.swiper-pagination-bullet {
width: 34px;
height: 34px;
text-align: center;
line-height: 34px;
font-size: 14px;
color: #000;
opacity: 1;
background: rgba(0, 0, 0, 0.3);
transition: background-color 0.5s ease, color 0.5s ease;
}
/* Swiper custom pagination */
.swiper-pagination-bullet:hover {
transition: background-color 0.5s ease;
background: rgba(0, 0, 0, 1);
color: white;
}
/* Swiper custom pagination active state */
.swiper-pagination-bullet-active {
color: #fff;
background: black;
}
/*==================================================================
CODEPEN STYLES -(under codepen gear/setting icon)
==============================================++++++================*/
.tippy-tooltip.light-theme {
color: #26323d;
box-shadow: 0 0 20px 4px rgba(154, 161, 177, 0.15),
0 4px 80px -8px rgba(36, 40, 47, 0.25),
0 4px 4px -2px rgba(91, 94, 105, 0.15);
background-color: white;
}
.tippy-backdrop {
background-color: white;
}
.tippy-roundarrow {
fill: white;
}
/* Default (sharp) arrow */
.tippy-popper[x-placement^='top'] .tippy-tooltip.light-theme .tippy-arrow {
border-top-color: #fff;
}
.tippy-popper[x-placement^='bottom'] .tippy-tooltip.light-theme .tippy-arrow {
border-bottom-color: #fff;
}
.tippy-popper[x-placement^='left'] .tippy-tooltip.light-theme .tippy-arrow {
border-left-color: #fff;
}
.tippy-popper[x-placement^='right'] .tippy-tooltip.light-theme .tippy-arrow {
border-right-color: #fff;
}
/* Round arrow */
.tippy-tooltip.light-theme .tippy-roundarrow {
fill: #fff;
}
/* TOC
part one - Swiper instilaze
part two - photoswipe instilaze
part three - photoswipe define options
part four - extra code (update swiper index when image close and micro changes)
/* 1 of 4 : SWIPER ################################### */
var mySwiper = new Swiper(".swiper-container", {
// If swiper loop is true set photoswipe counterEl: false (line 175 her)
loop: true,
/* slidesPerView || auto - if you want to set width by css like flickity.js layout - in this case width:80% by CSS */
slidesPerView: "auto",
spaceBetween: 10,
centeredSlides: true,
slideToClickedSlide: false,
autoplay: { /* remove/comment to stop autoplay */
delay: 3000,
disableOnInteraction: false /* true by deafult */
},
// If we need pagination
pagination: {
el: ".swiper-pagination",
clickable: true,
renderBullet: function(index, className) {
return '<span class="' + className + '">' + (index + 1) + "</span>";
}
},
// Navigation arrows
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
// keyboard control
keyboard: {
enabled: true,
}
});
// 2 of 4 : PHOTOSWIPE #######################################
// https://photoswipe.com/documentation/getting-started.html //
var initPhotoSwipeFromDOM = function(gallerySelector) {
// parse slide data (url, title, size ...) from DOM elements
// (children of gallerySelector)
var parseThumbnailElements = function(el) {
var thumbElements = el.childNodes,
numNodes = thumbElements.length,
items = [],
figureEl,
linkEl,
size,
item;
for (var i = 0; i < numNodes; i++) {
figureEl = thumbElements[i]; // <figure> element
// include only element nodes
if (figureEl.nodeType !== 1) {
continue;
}
linkEl = figureEl.children[0]; // <a> element
size = linkEl.getAttribute("data-size").split("x");
// create slide object
item = {
src: linkEl.getAttribute("href"),
w: parseInt(size[0], 10),
h: parseInt(size[1], 10)
};
if (figureEl.children.length > 1) {
// <figcaption> content
item.title = figureEl.children[1].innerHTML;
}
if (linkEl.children.length > 0) {
// <img> thumbnail element, retrieving thumbnail url
item.msrc = linkEl.children[0].getAttribute("src");
}
item.el = figureEl; // save link to element for getThumbBoundsFn
items.push(item);
}
return items;
};
// find nearest parent element
var closest = function closest(el, fn) {
return el && (fn(el) ? el : closest(el.parentNode, fn));
};
// triggers when user clicks on thumbnail
var onThumbnailsClick = function(e) {
e = e || window.event;
e.preventDefault ? e.preventDefault() : (e.returnValue = false);
var eTarget = e.target || e.srcElement;
// find root element of slide
var clickedListItem = closest(eTarget, function(el) {
return el.tagName && el.tagName.toUpperCase() === "LI";
});
if (!clickedListItem) {
return;
}
// find index of clicked item by looping through all child nodes
// alternatively, you may define index via data- attribute
var clickedGallery = clickedListItem.parentNode,
childNodes = clickedListItem.parentNode.childNodes,
numChildNodes = childNodes.length,
nodeIndex = 0,
index;
for (var i = 0; i < numChildNodes; i++) {
if (childNodes[i].nodeType !== 1) {
continue;
}
if (childNodes[i] === clickedListItem) {
index = nodeIndex;
break;
}
nodeIndex++;
}
if (index >= 0) {
// open PhotoSwipe if valid index found
openPhotoSwipe(index, clickedGallery);
}
return false;
};
// parse picture index and gallery index from URL (#&pid=1&gid=2)
var photoswipeParseHash = function() {
var hash = window.location.hash.substring(1),
params = {};
if (hash.length < 5) {
return params;
}
var vars = hash.split("&");
for (var i = 0; i < vars.length; i++) {
if (!vars[i]) {
continue;
}
var pair = vars[i].split("=");
if (pair.length < 2) {
continue;
}
params[pair[0]] = pair[1];
}
if (params.gid) {
params.gid = parseInt(params.gid, 10);
}
return params;
};
var openPhotoSwipe = function(
index,
galleryElement,
disableAnimation,
fromURL
) {
var pswpElement = document.querySelectorAll(".pswp")[0],
gallery,
options,
items;
items = parseThumbnailElements(galleryElement);
// #################### 3/4 define photoswipe options (if needed) ####################
// https://photoswipe.com/documentation/options.html //
options = {
/* "showHideOpacity" uncomment this If dimensions of your small thumbnail don't match dimensions of large image */
//showHideOpacity:true,
// Buttons/elements
closeEl: true,
captionEl: true,
fullscreenEl: true,
zoomEl: true,
shareEl: false,
counterEl: false,
arrowEl: true,
preloaderEl: true,
// define gallery index (for URL)
galleryUID: galleryElement.getAttribute("data-pswp-uid"),
getThumbBoundsFn: function(index) {
// See Options -> getThumbBoundsFn section of documentation for more info
var thumbnail = items[index].el.getElementsByTagName("img")[0], // find thumbnail
pageYScroll =
window.pageYOffset || document.documentElement.scrollTop,
rect = thumbnail.getBoundingClientRect();
return { x: rect.left, y: rect.top + pageYScroll, w: rect.width };
}
};
// PhotoSwipe opened from URL
if (fromURL) {
if (options.galleryPIDs) {
// parse real index when custom PIDs are used
// http://photoswipe.com/documentation/faq.html#custom-pid-in-url
for (var j = 0; j < items.length; j++) {
if (items[j].pid == index) {
options.index = j;
break;
}
}
} else {
// in URL indexes start from 1
options.index = parseInt(index, 10) - 1;
}
} else {
options.index = parseInt(index, 10);
}
// exit if index not found
if (isNaN(options.index)) {
return;
}
if (disableAnimation) {
options.showAnimationDuration = 0;
}
// Pass data to PhotoSwipe and initialize it
gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);
gallery.init();
/* ########### PART 4 - EXTRA CODE ########### */
/* EXTRA CODE (NOT FROM photoswipe CORE) -
1/2. UPDATE SWIPER POSITION TO THE CURRENT ZOOM_IN IMAGE (BETTER UI) */
// photoswipe event: Gallery unbinds events
// (triggers before closing animation)
gallery.listen("unbindEvents", function() {
// This is index of current photoswipe slide
var getCurrentIndex = gallery.getCurrentIndex();
// Update position of the slider
mySwiper.slideTo(getCurrentIndex, false);
// 2/2. Start swiper autoplay (on close - if swiper autoplay is true)
mySwiper.autoplay.start();
});
// 2/2. Extra Code (Not from photo) - swiper autoplay stop when image zoom */
gallery.listen('initialZoomIn', function() {
if(mySwiper.autoplay.running){
mySwiper.autoplay.stop();
}
});
};
// loop through all gallery elements and bind events
var galleryElements = document.querySelectorAll(gallerySelector);
for (var i = 0, l = galleryElements.length; i < l; i++) {
galleryElements[i].setAttribute("data-pswp-uid", i + 1);
galleryElements[i].onclick = onThumbnailsClick;
}
// Parse URL and open gallery if it contains #&pid=3&gid=1
var hashData = photoswipeParseHash();
if (hashData.pid && hashData.gid) {
openPhotoSwipe(hashData.pid, galleryElements[hashData.gid - 1], true, true);
}
};
// execute above function
initPhotoSwipeFromDOM(".my-gallery");
https://codepen.io/ezra_siton/pen/XNpJaX/
instead using photoswipe, use only swiper like in this demo I did make:
<-------html------>
<div class="swiper-container horizontal">
<div class="swiper-wrapper">
<div class="swiper-slide"><div class="swiper-container vertical">
<div class="swiper-wrapper vertical">
<div class="swiper-slide vertical">
Slide 1
</div>
<div class="swiper-slide vertical">
Slide 1.1
</div>
<div class="swiper-slide vertical">
Slide 1.2
</div>
<div class="swiper-slide vertical">
Slide 1.3
</div>
</div>
<div class="swiper-pagination vertical"></div>
</div></div>
<div class="swiper-slide">Slide 2</div>
<div class="swiper-slide">Slide 3</div>
<div class="swiper-slide">Slide 4</div>
<div class="swiper-slide">Slide 5</div>
<div class="swiper-slide">Slide 6</div>
<div class="swiper-slide">Slide 7</div>
<div class="swiper-slide">Slide 8</div>
<div class="swiper-slide">Slide 9</div>
<div class="swiper-slide">Slide 10</div>
</div>
<!-- Add Pagination -->
<div class="swiper-pagination horizontal"></div>
</div>
<!-- Swiper JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/3.3.1/js/swiper.min.js"></script>
<script>
var swiper = new Swiper('.swiper-container.horizontal', {
pagination: '.swiper-pagination.horizontal',
direction: 'horizontal',
slidesPerView: 1,
paginationClickable: true,
spaceBetween: 30,
mousewheelControl: true
});
</script>
<script>
var swiper = new Swiper('.swiper-container.vertical', {
pagination: '.swiper-pagination',
direction: 'vertical',
slidesPerView: 1,
paginationClickable: true,
spaceBetween: 30,
mousewheelControl: true
});
</script>
<---------html end----------->
<--------css----------->
html, body {
position: relative;
height: 100%;
}
body {
background: #eee;
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
font-size: 14px;
color:#000;
margin: 0;
padding: 0;
}
.swiper-container {
width: 100%;
height: 100%;
margin-left: auto;
margin-right: auto;
}
.swiper-slide {
text-align: center;
font-size: 18px;
background: #fff;
/* Center slide text vertically */
display: -webkit-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
}
<---------css end------------->
https://jsfiddle.net/120ngmoh/

using durandal and knockout but I can't set span text from a drop down change

I'm trying to bind a value to a span tag by changing the position on the drop down.
My JS file code is
define(['plugins/router', 'durandal/app', 'knockout', 'durandal/system'], function (router, app, ko, system) {
var Property = function (ref, title) {
this.ref = ref;
this.title = title;
};
var propertyList = [
new Property("0", "sample"),
new Property("1", "sasasfa"),
new Property("2", "jgpjijo"),
new Property("3", "uifhiuefh")
];
var items = ko.observableArray(propertyList);
var selectedProperty = ko.observable();
return {
router: router,
items: items,
selectedProperty: selectedProperty,
activate: function () {
router.map([
{ route: '', moduleId: 'viewmodels/propertydetails', title: 'Property Details', nav: true } ]).buildNavigationModel();
return router.activate();
}
};
});
My html is:
<div>
<div class="header-nav-items">
<ul class="nav" data-bind="foreach: router.navigationModel">
<li data-bind="css: { 'header-tab-active': isActive }">
<a data-bind="attr: { href: hash }, html: title"></a>
</li>
</ul>
</div>
<div style="background-color: #E05000; padding: 3px; height: 25px;">
<div style="float: left; margin-left: 10px; color: #ffffff;">
<span id="title" data-bind="text: selectedProperty() ? selectedProperty().title : 'Unknown'"></span>
</div>
<div style="float: right; margin-right: 10px;">
<select id="PropertyDDL" data-bind="options: items, optionsText: 'title', optionsValue: 'ref', value: selectedProperty, optionsCaption: 'Please select a property'"></select>
</div>
</div>
I'm completely new to using Durandal and knockount. I'm trying to set the text of the span tag with the value title from PropertyList using selectedProperty().title but the value appears blank when I change the drop down to any position greater than 0. At pos 0 it displays unknown. If I replace selectedProperty().title with selectedProperty() then the ref prints out correctly on the span text. Any ideas?
All is much more simple. optionsValue: 'ref' means that selectedProperty() is 0, 1, 2 or 3.
Therefore selectedProperty().title is undefined and text is empty.
If you want to use selectedProperty().title just remove optionsValue: 'ref' from select.
Try encapsulating the text for your title span in a computed observable. Like this:
var self = this;
self.titleText = ko.computed(function() {
var prop = self.selectedProperty();
return prop ? prop.title : 'Unknown';
});
<span id="title" data-bind="text: titleText"></span>
Knockout can be a bit quirky when you execute the observable in the binding expression itself, it doesn't always register the dependency properly such that the binding is notified when the underlying selectedProperty observable changes (in my experience).

jQuery Click on next element

I have a problem with programming jQuery and I hope that you can help me even if a speak english like an alien %).
I'm doing a site with horizontal scrolling, you can see it here http://www.kinetics.ru/index_kin.html
I have a menu at the left and two buttons PREV and NEXT at the bottom.
Menu is working correctly, but I need to let the bottom buttons work.
By clicking for example on NEXT I need that code made a click on next menu btn after the one that have style 'on'.
The code is:
<div class="menu">
<ul class="nav">
<li id="main"><img src="img/menu/about.png" /></li>
<li><img src="img/menu/uslugi.png"/></li>
<li><img src="img/menu/portfolio.png"/></li>
<li><img src="img/menu/clients.png"/></li>
<li><img src="img/menu/contacts.png"/></li>
</ul>
</div>
...
<div class="bottomNav" style="position: absolute; z-index: 11">
<div style="height: 26px; width:98px; margin:-75px 0 0 500px; position: fixed" class="back"><img src="img/back.png"</div>
<div style="height: 26px; width:114px; margin:-25px 0 0 600px; position: fixed" id="next"><img src="img/forward.png"</div>
And my not working jQuery code for next btn:
$('bottomNav, #next img').click(function(){
if ($('ul.nav').find('img').hasClass('on')){
$('ul.nav').next().click();
}
});
Also I've tried to do like this:
$('bottomNav, #next img').click(function(){
$('ul.nav img').click(function(){
if ($(this).hasClass('on')){
$(this).next().click();}
});
});
P.S. Sorry for my noobish question. I just got a task at work to make a site and nobody cares that a designer is NOT the same as web-designer. I have no possibility to learn first, I have to learn and do at same time.
You have to review jQuery selectors and as well your HTML code, it's terribly wrong. Firebug says:
I modified your code in proper way:
<div class="bottomNav" style="position: absolute; z-index: 11">
<div id="prev" style="height: 26px; width:98px; margin:-75px 0 0 500px; position: fixed">
<img src="img/back.png" />
</div>
<div id="next" style="height: 26px; width:114px; margin:-25px 0 0 600px; position: fixed" >
<img src="img/forward.png" />
</div>
</div>
JavaScript (It's pretty simple and I didn't test it, but it should give you an idea).
$('#prev').click(function () {
var $activeLi = getActiveLi();
var $prevLi = $parentLi.prev();
if ($prevLi.length > 0) {
$prevLi.find('a').click();
}
});
$('#next').click(function () {
var $activeLi = getActiveLi();
var $nextLi = $parentLi.next();
if ($nextLi.length > 0) {
$nextLi.find('a').click();
}
});
function getActiveLi() {
var $activeImg = $('.menu .nav img.on');
var $activeLi = $activeImg.closest('li');
return $activeLi;
};
But beeing seriously: before continuing developing your web site you seriously should put some effort in learnin hot to use jQuery and how to structure HTML...
Solved!
Thank you again!
Your code was really helpfull.
I have modified it a little bit for my needs and also changed in line
var $activeLi = $activeImg.closest('li');
closest('li') to parents('li'), cause it wasn't working like that.
So now code looks like this:
$('#prev').click(function () {
var $prevLi = getActiveLi().prev();
if ($prevLi.length > 0){$prevLi.find('img').click();}
});
$('#next').click(function () {
var $nextLi = getActiveLi().next();
if ($nextLi.length > 0){$nextLi.find('img').click();}
});
function getActiveLi() {
var $activeImg = $('.menu .nav img.on');
var $activeLi = $activeImg.parents('li');
return $activeLi;
};
Also i guess, that i have understood why firebug was showing so big disaster in code... i have forgot to close the tag :)))
Anyway...MANY THANKS TO YOU!
P.S. third day reading a giant book about jQuery :)