Capybara identify element that hosts shadow-root - capybara

I am trying to write a test that validates and confirms add-ons in a cart flow. One page I'm having difficulty with loads the page, and has a checkbox I need to hit that's within a shadow root. I can identify some of the highest level components on the page and am stuck trying to get to one in particular.
I have a byebug where I'm trying in the flow of the test and at the page in question.
Running "sales_channels_reg.regsaver_section.regsaver_testtest.text(:all)". returns:
"Show Progress Welcome Who Are You Registering? Information Review Suggested Add-Ons Shopping Cart Checkout Confirmation Zach Partyka Demo Site Suggested Add-Ons Loading... Loading... #displayBodyHeader { display: none !important } $j(document).ready(function () { listenForEventsFromSalesChannelsElement(); function listenForEventsFromSalesChannelsElement() { document.getElementById('sales-channels-element').addEventListener('outputEvent', function(event) { switch (event.detail.type) { case 'registration': registerAnotherOrContinueToCheckout(event.detail); break; case 'analytics': handleAnalyticsEvents(event.detail); break; } }); } function registerAnotherOrContinueToCheckout(eventDetail) { if (eventDetail.action === 'registerAnother') $j('.js-register-another-button').click(); else if (eventDetail.action === 'continue') $j('.js-continue-button').click(); } function handleAnalyticsEvents(eventDetail) { const railsEnv = 'production'; if (railsEnv === 'development') return; // SeMParticle only works for staging and production. switch (eventDetail.action) { case 'pageView': setCookie(\"surveySport\", eventDetail.pageAttributes.survey_sport , 1); setCookie(\"surveyType\", encodeURIComponent(eventDetail.pageAttributes.survey_type), 1); setCookie(\"surveyRegistrantType\", eventDetail.pageAttributes.survey_registrant_type, 1); setCookie(\"surveyParticipantType\", eventDetail.pageAttributes.survey_participant_type, 1); setCookie(\"surveyParticipantDOB\", eventDetail.pageAttributes.survey_participant_dob , 1); setCookie(\"surveyParticipantGender\", eventDetail.pageAttributes.survey_participant_gender, 1); setCookie(\"bossOrganizationId\", encodeURIComponent(eventDetail.pageAttributes.boss_organization_id), 1); setCookie(\"bossOrganizationName\", encodeURIComponent(eventDetail.pageAttributes.boss_organization_name), 1); setCookie(\"bossOrganizationAddrState\", encodeURIComponent(eventDetail.pageAttributes.boss_organization_addrState), 1); setCookie(\"bossOrganizationSports\", encodeURIComponent(eventDetail.pageAttributes.boss_organization_sports), 1); window.SeMParticle.pageView(eventDetail.pageAttributes); break; case 'logImpression': window.SeMParticle.logImpression(eventDetail.pageAttributes, eventDetail.productAttributes, 'SalesChannelsImpression'); break; case 'addToCart': window.SeMParticle.addToCart(eventDetail.pageAttributes, eventDetail.productAttributes); break; } } function setCookie(name,value,exp_days) { var d = new Date(); d.setTime(d.getTime() + (exp_days*24*60*60*1000)); var expires = \"expires=\" + d.toGMTString(); document.cookie = name + \"=\" + value + \";\" + expires + \";path=/;domain=.sportngin.com\"; } });"
But running "sales_channels_reg.regsaver_section.regsaver_testtest.find(:css, "#sales-channels-element__container").text(:all)" returns only the "Loading... Loading..." text above the shadow-root div
The part I need help with is I don't know why I can't I dig to the div element that has the shadow-root? I've tried using find_element for :id, :css, lots of "sleep()" to let the page load, etc., but still stuck there
The Code for trying to target the test in particular:
-first file-
describe "Showing 4 max add-ons in reg flow", type: :feature, service: "sales_channels1" do
context "yadda yadda" do
subject(:sales_channels_reg) { SalesChannelsRegGenerals.new }
subject(:se_signup_page) { SELogin.new }
# subject(:gen_reg) { RegistrationHelper.new }
let(:form_number) { "848624247" }
it "in test flow does thing" do
couple of other steps first
byebug (where I'm at in the code now)
end
end
end
-different file-
require "./spec/page_models/sales_channels_ncsa_section.rb"
require "./spec/page_models/sales_channels_medsaver_section.rb"
require "./spec/page_models/sales_channels_regsaver_section.rb"
require "./spec/page_models/sales_channels_four_addons_section.rb"
class SalesChannelsRegGenerals < SitePrism::Page
set_url "https://zachpartyka#{SeleniumTest.ngin_site}/register/form/{/form_number}"
section :regsaver_section, RegSaverSection, "div#siteContainer2"
end
-different file-
class RegSaverSection < SitePrism::Section
element :regsaver_testtest, "#pageContentContainer"
end
The HTML for this section is (see attached pic for clearer picture):
<div id="pageContentContainer" class="clearfix" xpath="1">
::before[![enter image description here][1]][1]
<div id="panelOne">
<div class="subNavContainer">
<div id="sequenceMenu">
<div id="sub-nav-toggle" class="sub-nav-toggle">
<span class="sub-nav-toggle-text">Show Progress</span>
</div>
<div class="subNav noHeader">
<ul id="formsBin" class="steps">
<li class="enabled complete">Welcome</li>
<li class="enabled complete">Who Are You Registering?</li>
<li id="form_2959222" class="enabled complete">Information</li>
<li class="enabled complete">Review</li>
<li class="enabled selected"><span>Suggested Add-Ons</span></li>
<li class="enabled incomplete"><span>Shopping Cart</span></li>
<li class="enabled incomplete"><span>Checkout</span></li>
<li class="enabled incomplete"><span>Confirmation</span></li>
</ul>
</div>
</div>
</div>
</div>
<div id="panelTwo">
<div id="displayBodyContent">
<div id="flashNotice">
</div>
<div id="yieldContent">
<h2 id="displayBodyHeader" class=""><span>Zach Partyka Demo Site Suggested Add-Ons</span></h2>
<div id="sales-channels-element__container" style="">
<sales-channels-element id="sales-channels-element" context="{"multipleRegistrations":{"enabled":true,"buttonLabel":"Register Another person"},"mode":"user","userId":"49dd822a5d654085a5e4b435b83265cc","bossOrganization":{"id":230482,"name":"Sales Channels Test Org"},"guestKey":"9553ee6b1c99c47deda474f5fb09a074c8820b6188b1ac7031f4452546386b27","enabledSalesChannels":["MedSaver","Skills","PitchTrackerBaseball","PitchTrackerSoftball","RegSaver","Ncsa"],"participant":{"firstName":"baseballpitchtracker","lastName":"child","gender":"male","dob":"01/01/2006","address":{"address1":"45645 buttsville","address2":null,"state":"mn","city":"burning","zip":"55418","country":"US"},"email":"selenium+actionable_redirect#sportngin.com","phoneNumber":"6515555555","graduationYear":null},"registrant":{"firstName":"Actionable","lastName":"Redirect","dob":"01/01/1990","address":{"address1":"2345 Foo Bar Circle","address2":null,"state":"MN","city":"Testingville","zip":"55428","country":"US"},"email":"selenium+actionable_redirect#sportngin.com","phoneNumber":"6515555555"},"requestDetails":{"eventType":"Multiple","eventCost":10.0,"programStartDate":"12/22/2021","programEndDate":"12/22/2022","paymentDate":"12/21/2021","sport":"Baseball","submittedBy":"parent"},"analyticsPageAttributes":{"depth1":"Sitebuilder","depth2":"Registration","depth3":"SalesChannels","survey_sport":"Baseball","survey_type":"Event","survey_registrant_type":"parent","survey_participant_type":"athlete","survey_participant_dob":"01/01/2006","survey_participant_gender":"male"},"analyticsEventAttributes":{"event_action_sports":"Baseball","event_action_genders":"male","event_action_dobs":"01/01/2006","event_type":1,"action":"Click","bossOrganizationId":230482,"bossOrganizationName":"Sales Channels Test Org","bossOrganizationAddrState":"MN","bossOrganizationSports":"baseball"}}" ng-version="10.2.5" style=""></sales-channels-element>
<div style="display: none;">
<br>
<div class="pageElement">
<div class="surveyButtonWrap" style="margin-left:0;text-align: center;">
<div style="display: inline-block;text-align: right;">
<form class="button_to" method="post" action="/survey/register_another_person/591168"><input waiting_class="submitProcess pageElement" style="text-transform:capitalize;" class="button-large js-register-another-button" id="processing_link" type="submit" value="Register Another person"><input type="hidden" name="authenticity_token" value="lBrd9OZaA7siTgHe8WYjV3LSqYKUGthsNloEKiMMVeypQUcTUVq/AxLjEjQv0i+Y79R4YPQOMcrlGGwxos/gJw=="></form><div id="waiting_textprocessing_link" style="display:none;" class="pl-overlay"><div class="pl-progress__container"><p class="pl-progress__label">Loading...</p><div class="pl-progress"><div role="progressbar" class="pl-progress__bar--indeterminate"></div></div></div></div>
</div>
<div style="display: inline-block; text-align: left;">
<form class="button_to" method="post" action="/cart/add?from_step=85"><input class="button-large button-siteColor button-default js-continue-button" id="processing_link" type="submit" value="Continue"><input type="hidden" name="authenticity_token" value="lBrd9OZaA7siTgHe8WYjV3LSqYKUGthsNloEKiMMVeypQUcTUVq/AxLjEjQv0i+Y79R4YPQOMcrlGGwxos/gJw=="></form><div id="waiting_textprocessing_link" style="display:none;" class="pl-overlay"><div class="pl-progress__container"><p class="pl-progress__label">Loading...</p><div class="pl-progress"><div role="progressbar" class="pl-progress__bar--indeterminate"></div></div></div></div>
</div>
</div>
</div>
<div class="clearAll"></div>
</div>
</div>
<style>#displayBodyHeader { display: none !important }</style>
<script type="text/javascript" src="https://se-sales-channels.ui.sportsengine.com/sales-channels-element.js"></script>
<script>
$j(document).ready(function () {
listenForEventsFromSalesChannelsElement();
function listenForEventsFromSalesChannelsElement() {
document.getElementById('sales-channels-element').addEventListener('outputEvent', function(event) {
switch (event.detail.type) {
case 'registration':
registerAnotherOrContinueToCheckout(event.detail);
break;
case 'analytics':
handleAnalyticsEvents(event.detail);
break;
}
});
}
function registerAnotherOrContinueToCheckout(eventDetail) {
if (eventDetail.action === 'registerAnother') $j('.js-register-another-button').click();
else if (eventDetail.action === 'continue') $j('.js-continue-button').click();
}
function handleAnalyticsEvents(eventDetail) {
const railsEnv = 'production';
if (railsEnv === 'development') return; // SeMParticle only works for staging and production.
switch (eventDetail.action) {
case 'pageView':
setCookie("surveySport", eventDetail.pageAttributes.survey_sport , 1);
setCookie("surveyType", encodeURIComponent(eventDetail.pageAttributes.survey_type), 1);
setCookie("surveyRegistrantType", eventDetail.pageAttributes.survey_registrant_type, 1);
setCookie("surveyParticipantType", eventDetail.pageAttributes.survey_participant_type, 1);
setCookie("surveyParticipantDOB", eventDetail.pageAttributes.survey_participant_dob , 1);
setCookie("surveyParticipantGender", eventDetail.pageAttributes.survey_participant_gender, 1);
setCookie("bossOrganizationId", encodeURIComponent(eventDetail.pageAttributes.boss_organization_id), 1);
setCookie("bossOrganizationName", encodeURIComponent(eventDetail.pageAttributes.boss_organization_name), 1);
setCookie("bossOrganizationAddrState", encodeURIComponent(eventDetail.pageAttributes.boss_organization_addrState), 1);
setCookie("bossOrganizationSports", encodeURIComponent(eventDetail.pageAttributes.boss_organization_sports), 1);
window.SeMParticle.pageView(eventDetail.pageAttributes);
break;
case 'logImpression':
window.SeMParticle.logImpression(eventDetail.pageAttributes, eventDetail.productAttributes, 'SalesChannelsImpression');
break;
case 'addToCart':
window.SeMParticle.addToCart(eventDetail.pageAttributes, eventDetail.productAttributes);
break;
}
}
function setCookie(name,value,exp_days) {
var d = new Date();
d.setTime(d.getTime() + (exp_days*24*60*60*1000));
var expires = "expires=" + d.toGMTString();
document.cookie = name + "=" + value + ";" + expires + ";path=/;domain=.sportngin.com";
}
});
</script>
</div>
<div class="clearAll"></div>
</div>
</div>
</div>

You need to use evaluate_script rather than execute_script or the returned value won't get unwrapped into an element that can be used.
evaluate_script("arguments[0].shadowRoot", ...)

As it turns out, I didn't do a thorough enough investigation. The element was not visible, and I didn't try using visible? along with find_element. The solution to selecting the shadow root - and then digging within it was as follows:
sales_channels_reg.execute_script("return arguments[0].shadowRoot", sales_channels_reg.regsaver_section.regsaver_testtest.find("#sales-channels-element", visible: false)).find('#addToCart--false')

Related

jquery / Ajax "undefined" error

i am trying to get an image uploader working inside my MVC project, but everytime i click the upload file button, i get "Error undefined", i am not sure where i am going wrong.
The page is a partial view, loaded inside a #Html.BeginForm . below is the code i am using.
Index.cshtml
#using (Html.BeginForm("Register", "Account", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="container">
<div class="row">
<article class="col-sm-12 col-md-12 col-lg-12 sortable-grid ui-sortable">
<div id="wid-id-0">
<!-- widget div-->
<div role="content">
<!-- widget content -->
<div class="widget-body">
<div class="row" style="background: white">
<form id="wizard-1" novalidate="novalidate">
<div id="bootstrap-wizard-1" class="col-sm-12">
<div class="tab-content">
#*Tab 1 (Step 1)*#
<div class="tab-pane active" id="tab1">
<br>
#Html.Partial("Registration/_Step1")
<div class="form-actions">
<div class="row">
<div class="col-sm-12">
<ul class="pager wizard no-margin">
<li class="previous disabled">
Previous
</li>
<li data-target="#step2" class="next">
Next
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="form-bootstrapWizard">
<ul class="bootstrapWizard form-wizard" style="background: coral">
<li class="active" data-target="#step1">
<span class="step">1</span> <span class="title">Step 1: Profile Information</span>
</li>
<li data-target="#step2" class="">
<span class="step">2</span> <span class="title">Step 2: Profile Picture</span>
</li>
<li data-target="#step3" class="">
<span class="step">3</span> <span class="title">Step 3: Identification</span>
</li>
<li data-target="#step4">
<span class="step">4</span> <span class="title">Step 4: Submit Profile</span>
</li>
</ul>
<div class="clearfix"></div>
</div>
</div>
</form>
</div>
</div>
<!-- end widget content -->
</div>
<!-- end widget div -->
</div>
<!-- end widget -->
</article>
</div>
</div>
}
_Step1.cshtml
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
$(document).ready(function () {
$("#Upload").click(function (e) {
e.preventDefault();
var formData = new FormData();
var totalFiles = document.getElementById("FileUpload").files.length;
for (var i = 0; i < totalFiles; i++)
{
var file = document.getElementById("FileUpload").files[i];
formData.append("FileUpload", file);
}
$.ajax({
type: "POST",
url: '#Url.Action("UploadFiles", "Account")',
data: formData,
dataType: 'json',
contentType: false,
processData: false,
success: function (response) {
alert('succes!!');
},
error: function (error) {
alert("error: " + error.statusMessage);
}
});
});
});
</script>
<table align="center">
<tr>
<td style="text-align: center;"><label>Upload Profile Picture</label></td>
<td></td>
</tr>
<tr>
<td valign="top"><img id="imgPreview" src="~/Content/Images/upload_holder.PNG" class="img-holder" style="width: 180px; height: 180px;" /></td>
<td>
#*<input type="submit" class="btn btn-lg" style="width: 200px; background: #0091d9; color: white;" value="Upload Photo" />*#
#*<input id="input-4" name="input4[]" type="file" multiple class="file-loading" style="width: 200px; background: #0091d9; color: white;" value="Upload Photo">*#
<input type="file" id="FileUpload" multiple />
<input type="submit" id="Upload" value="Upload"/>
<br />
<br />
<label>Acceptable file formats: jpg, gif or png file</label>
<br />
<label>Under 1 MB</label>
</td>
</tr>
</table>
any help is greatly appreciated!
EdIT: Below is the controller function, but its never hit.
[HttpPost]
public ActionResult UploadFiles()
{
// Checking no of files injected in Request object
if (Request.Files.Count > 0)
{
try
{
// Get all files from Request object
HttpFileCollectionBase files = Request.Files;
for (int i = 0; i < files.Count; i++)
{
//string path = AppDomain.CurrentDomain.BaseDirectory + "Uploads/";
//string filename = Path.GetFileName(Request.Files[i].FileName);
HttpPostedFileBase file = files[i];
string fname;
// Checking for Internet Explorer
if (Request.Browser.Browser.ToUpper() == "IE" || Request.Browser.Browser.ToUpper() == "INTERNETEXPLORER")
{
string[] testfiles = file.FileName.Split(new char[] { '\\' });
fname = testfiles[testfiles.Length - 1];
}
else
{
fname = file.FileName;
}
// Get the complete folder path and store the file inside it.
fname = Path.Combine(Server.MapPath("~/Uploads/"), fname);
file.SaveAs(fname);
}
// Returns message that successfully uploaded
return Json("File Uploaded Successfully!");
}
catch (Exception ex)
{
return Json("Error occurred. Error details: " + ex.Message);
}
}
else
{
return Json("No files selected.");
}
}

Adding active class to dynamic Twitter Bootstrap carousel in ASP.NET MVC

I'm using the Boostrap 2.x slider to display a series of testimonials on a webpage which I retrieve from a database.
My code is as follows:
<div id="testimonials" class="carousel slide">
<div class="carousel-inner">
#foreach (var item in Model.Testimonials)
{
<div class="item" style="max-height:70px; overflow:hidden">
<h4>#item.Title</h4>
#item.Quote
</div>
}
</div>
</div>
For the slider to work, the first item needs to be set to 'active', otherwise the first item shown is blank. How do I add the 'active' class to the first item in MVC?
So the first item should display as
<div class="item active" style="max-height:70px; overflow:hidden">
instead of
<div class="item" style="max-height:70px; overflow:hidden">
In my script file I have:
$(document).ready(function () {
$('#testimonials').carousel({
interval: 6000
});
$('#testimonials').carousel('cycle');
}
(there are 2 sliders on this page)
Thanks.
You can check for the first item in the loop:
<div id="testimonials" class="carousel slide">
<div class="carousel-inner">
#{
var i = 0;
foreach (var item in Model.Testimonials)
{
var itemClass = i++ == 0 ? "item active" : "item";
<div class="#itemClass" style="max-height:70px; overflow:hidden">
<h4>#item.Title</h4>
#item.Quote
</div>
}
}
</div>
</div>
or replace foreach with for loop if your Model.Testimonials property is a type of IList<T> or any collection, which items can be individually accessed by index:
<div id="testimonials" class="carousel slide">
<div class="carousel-inner">
#for (int i = 0; i < Model.Testimonials.Count; i++)
{
var item = Model.Testimonials[i];
var itemClass = i == 0 ? "item active" : "item";
<div class="#itemClass" style="max-height:70px; overflow:hidden">
<h4>#item.Title</h4>
#item.Quote
</div>
}
</div>
</div>
If you want to do this with JavaScript, so:
$(document).ready(function () {
$('.carousel-inner .item:first').addClass('active');
$('#testimonials').carousel({
interval: 6000
});
$('#testimonials').carousel('cycle');
}
Also I would suggest you to move the style="max-height:70px; overflow:hidden" line to css class:
.carousel-inner .item {
max-height: 70px;
overflow: "hidden";
}

Pagination for dynamic data in div in asp .net

How can i use pagination for showing data dynamically in div in asp .net using ajax or jquery?
To be honest, it's hard to help you - you should be more specific, but maybe you are looking for sth like this:
jQuery pagination plugin
and
demo here
You haven't really asked a real question, but maybe this will help: https://github.com/TroyGoode/PagedList
You can create pagination on your div using bootstrap and jquery.
Controller
public ActionResult Index()
{
// Tab Data
ThumbnailViewModel model = new ThumbnailViewModel();
model.ThumbnailModelList = new List<ThumbnailModel>();
// Test Details Data
List<ThumbnailDetails> _detaisllist = new List<ThumbnailDetails>();
int count = 10;
for (int i = 1; i <= count; i++)
{
ThumbnailDetails obj = new ThumbnailDetails();
obj.Details1 = "Details- Main" + i;
obj.Details2 = "Details- Main-Sub" + i;
_detaisllist.Add(obj);
}
// batch your List data for tab view i want batch by 2 you can set your value
var listOfBatches = _detaisllist.Batch(2);
int tabNo = 1;
foreach (var batchItem in listOfBatches)
{
// Generating tab
ThumbnailModel obj = new ThumbnailModel();
obj.ThumbnailLabel = "Lebel" + tabNo;
obj.ThumbnailTabId = "tab" + tabNo;
obj.ThumbnailTabNo = tabNo;
obj.Thumbnail_Aria_Controls = "tab" + tabNo;
obj.Thumbnail_Href = "#tab" + tabNo;
// batch details
obj.ThumbnailDetailsList = new List<ThumbnailDetails>();
foreach (var item in batchItem)
{
ThumbnailDetails detailsObj = new ThumbnailDetails();
detailsObj = item;
obj.ThumbnailDetailsList.Add(detailsObj);
}
model.ThumbnailModelList.Add(obj);
tabNo++;
}
// Getting first tab data
var first = model.ThumbnailModelList.FirstOrDefault();
// Getting first tab data
var last = model.ThumbnailModelList.LastOrDefault();
foreach (var item in model.ThumbnailModelList)
{
if (item.ThumbnailTabNo == first.ThumbnailTabNo)
{
item.Thumbnail_ItemPosition = "first";
}
if (item.ThumbnailTabNo == last.ThumbnailTabNo)
{
item.Thumbnail_ItemPosition = "last";
}
}
return View(model);
}
View:
#model ThumbnailPagination.Models.ThumbnailViewModel
#{
ViewBag.Title = "Home Page";
}
<div class="container">
<div class="col-xs-10 col-md-6 col-xs-offset-1 col-md-offset-3">
<div class="row">
<nav aria-label="...">
<ul class="pager" role="tablist">
<li class="previous" onclick="goTo(1);"><span aria-hidden="true">←</span> Previous</li>
#{
foreach (var item in Model.ThumbnailModelList)
{
if (item.Thumbnail_ItemPosition == "first")
{
<li class="active" id="#item.Thumbnail_ItemPosition">
<a aria-controls="#item.Thumbnail_Aria_Controls" data-toggle="tab" href="#item.Thumbnail_Href" role="tab">#item.ThumbnailTabNo</a>
</li>
}
else if (item.Thumbnail_ItemPosition == "last")
{
<li id="#item.Thumbnail_ItemPosition">
<a aria-controls="#item.Thumbnail_Aria_Controls" data-toggle="tab" href="#item.Thumbnail_Href" role="tab">#item.ThumbnailTabNo</a>
</li>
}
else
{
<li>
<a aria-controls="#item.Thumbnail_Aria_Controls" data-toggle="tab" href="#item.Thumbnail_Href" role="tab">#item.ThumbnailTabNo</a>
</li>
}
}
}
<li class="next" onclick="goTo(2);">Next <span aria-hidden="true">→</span></li>
</ul>
</nav>
</div>
<!-- Tab panes -->
<div class="tab-content">
#{
foreach (var item in Model.ThumbnailModelList)
{
if (item.Thumbnail_ItemPosition == "first")
{
<div class="tab-pane active" id="#item.ThumbnailTabId" role="tabpanel">
#{
foreach (var detailsitem in item.ThumbnailDetailsList)
{
<div class="col-sm-6">
<div class="thumbnail">
<img alt="..." src="http://placehold.it/240x150">
<div class="caption">
<h3>#detailsitem.Details1</h3>
<p>
#detailsitem.Details2
</p>
<p>
<a class="btn btn-primary" href="#" role="button">
Read more
...
</a>
</p>
</div>
</div>
</div>
}
}
</div>
}
else
{
<div class="tab-pane" id="#item.ThumbnailTabId" role="tabpanel">
#{
foreach (var detailsitem in item.ThumbnailDetailsList)
{
<div class="col-sm-6">
<div class="thumbnail">
<img alt="..." src="http://placehold.it/240x150">
<div class="caption">
<h3>#detailsitem.Details1</h3>
<p>
#detailsitem.Details2
</p>
<p>
<a class="btn btn-primary" href="#" role="button">
Read more
...
</a>
</p>
</div>
</div>
</div>
}
}
</div>
}
}
}
</div>
</div>
</div>
<style>
.pager .active a {
background-color: #337AB7;
color: #FFF;
border: 0px;
}
</style>
<script>
function goTo(number) {
$('ul.pager li:eq(' + number + ') a').tab('show');
upgradePreNext(number);
}
function upgradePreNext(number) {
if (number > 1) {
$('ul.pager li:eq(0)').attr("onclick", "goTo(" + (number - 1) + ")");
$('ul.pager li:eq(0)').attr("class", "previous");
} else {
$('ul.pager li:eq(0)').attr("class", "disabled");
}
if (number < 5) {
$('ul.pager li:eq(6)').attr("onclick", "goTo(" + (number + 1) + ")");
$('ul.pager li:eq(6)').attr("class", "next");
} else {
$('ul.pager li:eq(6)').attr("class", "disabled");
}
}
$(document).ready(function () {
$('li a').on('click', function (e) {
goTo((e.target.innerHTML) - 0);
});
});
</script>
Out put will be:
You can also download the sample code.

jquerymobile - navigation in multi-page template

I have a multi-page template set up. The first page has a set of links to the inner pages - however all but one link to the same #template page which loads data dynamically. The other link is to a form which I have built within the page.
Problem I'm having is when you navigate to one of the links, continue through the pages and then use the back button to get back to the first page, if you then click the link to the form it doesn't navigate to the #page, it goes to the last page you were on in the other links.
Hope that makes sense - maybe a diagram will make it clearer
home > product list > product detail
product detail > product list > home (back button)
home > contact form
This last step shows the product detail page again, instead of the contact form. It's as if it's not clearing the page out:
My code (truncated for clarity):
<script type="text/javascript">
$(document).bind("mobileinit", function () {
$.mobile.allowCrossDomainPages = true;
$.support.cors = true;
});
$(document).delegate("#pageDetail", "pagecreate", function () {
$(this).css('background', '#ECF2FE');//`this` refers to `#pageDetail`
});
$(document).bind("pagebeforechange", function (e, data) {
// We only want to handle changePage() calls where the caller is
// asking us to load a page by URL.
if (typeof data.toPage === "string") {
$.mobile.pageData = (data && data.options && data.options.pageData) ? data.options.pageData : null;
// We are being asked to load a page by URL, but we only
// want to handle URLs that request the data for a specific
// category.
var page;
var type;
var cat;
if ($.mobile.pageData && $.mobile.pageData.type) {
type = $.mobile.pageData.type;
}
if ($.mobile.pageData && $.mobile.pageData.cat) {
cat = $.mobile.pageData.cat;
}
if ($.mobile.pageData && $.mobile.pageData.page) {
page = $.mobile.pageData.page;
}
var url = $.url(data.toPage); // page url
var hash = url.fsegment(1).replace(/&.*/, ""); // nav hash for page holder
switch (hash) {
case "pageList":
$.ajax({
url: "http://localhost/myapp/" + type + ".aspx?type=" + type,
datatype: "html",
success: function (data) {
$('.submenu').html(data);
$('.title').html(type);
$('.submenu').trigger('create');
}
});
break;
case "pageDetail":
$('.detail').load('http://localhost/myapp/' + type + '.aspx?page=' + page + '&type=' + type + ' #contentdiv', function () {
$(this).trigger('create');
});
break;
default:
break;
}
}
});
</script>
</head>
HTML bits
<body>
<div data-role="page" id="home">
<div data-role="content">
<img src="images/Icon-Courses.png" alt="courses" /><br />
<img src="images/Icon-Contact.png" alt="contact" />
</div>
</div>
<div data-role="page" data-theme="a" id="pageList" data-add-back-btn="true" style="background-color:#F0F0F0 !important;">
<div data-role="header" data-position="fixed">
<h1 class="title">Products</h1>
</div>
<div data-role="content" class="submenu">
<!-- content gets put in here -->
</div>
</div>
<div data-role="page" data-theme="a" id="pageDetail" data-add-back-btn="true" style="background-color:#F0F0F0 !important;">
<div data-role="header" data-position="fixed">
<h1 class="title">Products</h1>
</div>
<div data-role="content" class="submenu">
<!-- content gets put in here -->
</div>
</div>
<div data-role="page" data-theme="a" id="contactForm" data-add-back-btn="true" class="detailpage" style="background-color:#F0F0F0 !important;">
<div data-role="header" data-position="fixed">
<h1 class="title">Contact Us</h1>
</div>
<div data-role="content" class="detail">
<div id="contentdiv" style="margin:10px auto;width:90%;">
<label for="name">Your Name</label>
<input type="text" id="name" style="width:90%;" data-mini="true" />
<label for="email">Email Address</label>
<input type="text" id="email" style="width:90%;" data-mini="true" />
<label for="phone">Phone Number</label>
<input type="text" id="phone" style="width:90%;" data-mini="true" />
<label for="comments">Enquiry / Comments</label>
<textarea id="comments" rows="80" style="width:90%;" data-mini="true"></textarea>
<br />
<input type="submit" id="submit" value="Send Enquiry Now" />
</div>
</div>
</div>
</body>
</html>
I'm using the jqm.page.params.js for handling the querystring parameters.
Is there anything obvious there? The links to the #pageDetail page are returned within the content shown on #pageList.
Don't understand why I can't seem to clear the cache and it doesn't navigate correctly to the contact form if you've been anywhere else first. if you go straight there it works fine.
Anyone shed any light? BTW this needs to work using PhoneGap as a standalone
Thanks
Actually feel a bit dim about this, but it's because I had the same class names on multiple divs and it was using those to import data - so when it was loading data remotely it was putting it in more than one place, overwriting the form at the same time...

content of dynamically-added jQuery-ui tabs only showing once

I'm trying to create a tab set using jQuery UI that has some permanent tabs as well as some special purpose tabs. The special tabs are added temporarily: when the form they contain is submitted, the tabs are removed.
I've got this working except for one thing: after a tab is removed, if it is re-added later its content isn't shown, and I can't figure out why. I've distilled it down to this jsFiddle example, code also reposted below.
HTML:
<div id="tabs">
<ul>
<li>Foo</li>
</ul>
<div id="foo">
<h2>Foo Tab</h2>
</div>
<div id="bar" class="transient" style="display: none">
<h2><button type="button" class="close" style="float: right"><span class="ui-icon ui-icon-closethick">close</span></button>Bar Tab</h2>
</div>
<div id="baz" class="transient" style="display: none">
<h2><button type="button" class="close" style="float: right"><span class="ui-icon ui-icon-closethick">close</span></button>Baz Tab</h2>
</div>
</div>
<hr>
<button onClick="openTransientTab('bar', 'Bar')">Add Bar Tab</button>
<button onClick="openTransientTab('baz', 'Baz')">Add Baz Tab</button>
JavaScript:
$('#tabs').find('div.transient').find(".close").live('click', function() {
var footer_tabs = $('#tabs');
var tab_id = $(this).closest("div.transient").attr("id");
var index = footer_tabs.tabs("option", "selected");
footer_tabs.tabs("select", -1);
footer_tabs.tabs("remove", index);
});
function openTransientTab(id, title) {
var footer_tabs = $("#tabs");
footer_tabs.tabs("select", -1);
footer_tabs.tabs("select", "#" + id);
var selected = footer_tabs.tabs("option", "selected");
if (selected < 0) {
footer_tabs.tabs("add", "#" + id, title);
footer_tabs.tabs("select", "#" + id);
}
$("#" + id).css("display", "block");
}
$(function() {
var footer_tabs = $("#tabs");
footer_tabs.tabs({
collapsible: true,
selected: -1
});
});
When you load the page, the bar and baz tabs are created, but in their style, display is set to none which is why they are not visible originally. Inside the tab, when you hit the X, it actually removes the div for bar and baz completely. When you re-click to add the bar or baz tab after it closes, it recreates the div, but you are not putting anything within it. Add something like the following to once you create the tab.
document.getElementById("bar").innerHtml = whatever you want within it here
Before:
<div id="foo" class="ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide">
<h2>Foo Tab</h2>
</div>
<div id="baz" class="transient" style="display: none">
<h2>
<button class="close" style="float: right" type="button">
<span class="ui-icon ui-icon-closethick">close</span>
</button>
Baz Tab
</h2>
</div>
<div id="bar" class="transient ui-tabs-panel ui-widget-content ui-corner-bottom" style="display: block;">
<h2>
<button class="close" style="float: right" type="button">
<span class="ui-icon ui-icon-closethick">close</span>
</button>
Bar Tab
</h2>
</div>
After opening and closing both
<div id="foo" class="ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide">
<h2>Foo Tab</h2>
</div>
<div id="bar" class="ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide" style="display: block;"></div>
<div id="baz" class="ui-tabs-panel ui-widget-content ui-corner-bottom" style="display: block;"></div>
After Collecter provided the key insight about what wasn't working, I found a nicer way to preserve the tab content for reuse. I changed my close function to the following:
$('#tabs').find('div.transient').find(".close").live('click', function() {
var footer_tabs = $('#tabs');
var tab = $(this).closest("div.transient");
var index = footer_tabs.tabs("option", "selected");
footer_tabs.tabs("select", -1);
footer_tabs.tabs("remove", index);
footer_tabs.append(tab);
});

Resources