Re-running a jQuery plugin against updated DOM - javascript

I'm using a jQuery grid slider plugin that I'd like to run again after DOM manipulation.
I suspect my issue is similar to this - Any way to rerun a jQuery plugin after DOM modification? - but my attempts aren't work.
Here's the setup:
When the page loads, the grid plugin js and the options js run against an unordered list, like such:
<div class="panel">
<div class="g-container">
<div class="g-slider">
<div class="tiles">
<ul>
<li><img src="Images/image-01.jpg"><div><h1>automotive</h1></div></li>
<li><img src="Images/image-03.jpg"><div><h1>electronics</h1></div></li>
<li><img src="Images/image-02.jpg"><div><h1>medical</h1></div></li>
<li><img src="Images/image-11.jpg"><div><h1>automotive</h1></div></li>
<li><img src="Images/image-02.jpg"><div><h1>automotive</h1></div></li>
<li><img src="Images/image-09.jpg"><div><h1>medical</h1></div></li>
<li><img src="Images/image-11.jpg"><div><h1>electronics</h1></div></li>
</ul>
</div>
</div>
</div>
</div>
...and create the grid slider. Here's an example of what it looks like: http://1.s3.envato.com/files/27300390/index.html
This all works fine.
(Long story short) I have a large list of categories that are all grouped together that I've placed in a div holder, like so:
<div id="listing-holder">
<ul>
<li><img src="Images/car-01.jpg"><div><h1>automotive</h1></div></li>
<li><img src="Images/car-01.jpg"><div><h1>electronics</h1></div></li>
...
</ul>
</div>
These categories are dynamically created and in one large (hidden off the page) group. So there may be, say, 20 categories, and 100 items (in my example, the images). The items may appear in more than one category, though I only want to show one at a time.
Elsewhere on the page are some navigation links. When one of these links is clicked, the items in #listing-holder are copied into .tiles ul. Then it's name is checked against the header tags in .tiles ul, removing any items that do not match. Then, I want the plugin to run again so that the pagination and slider work as before the change, but with new paging (if it's more or less). Like so:
$("#left-menu").delegate("a", "click", function(e) {
var itemClicked;
itemClicked = $(this).text();
$('.g-container .g-slider .tiles').html('');
$('.g-container .g-slider .tiles').html($('#listing-holder').html());
$(".g-container .g-slider .tiles h1:not(:contains(" + itemClicked + "))").closest('li').remove();
init(); // this isn't working the way I expected it to
});
Here's the (pertinent information from the) options js:
$(document).ready(
function() {
var $panel = $(".panel");
var $container = $panel.find(".g-container");
... // more variables
$(".g-container").gridSlider({
num_cols:5,
num_rows:3,
... // more settings
});
$submitButton.click(function() {
$container.undoChanges()
.setGridSize($gridCol.val(),$gridRow.val())
.setNoCategory($categories.filter(":checked").val() == "no")
... // more settings
.updateChanges(); // this is in the grid.js
setPanelWidth();
});
$resetButton.click(function() {
init();
$submitButton.trigger("click");
});
var init = function() {
$gridCol.val(5);
$gridRow.val(3);
... // more settings
}
var setPanelWidth = function() {
var width = $container.find(">div").outerWidth();
if (width < panelWidth) {
width = panelWidth;
}
$panel.css({width:width, marginLeft:-width/2});
... // more settings
}
init();
}
);
I've tried calling the init function, which does call it, but nothing seems to happen. I placed alerts at the beginning and end of the grid.js (which is minified), and they both fired, so it seems like it's going through.
I thought I'd have to use something like this:
(function( $ ){
$.fn.myPlugin = (function(){
{
// do something...
}
});
})(jQuery);
...but since I was able to call init from the options.js, I didn't go that route (but I'm willing to re-visit this, as I may have implented it incorrectly).
I've been at this for about a week, and I suspect I'm approaching this wrong, and could use some direction.
I think I could resolve the issue by creating all of the categories, then using something like getScript() to run afterwards. I think that would work. The issue is that there may potentially be several dozen categories, with items that may appear in several.
I figured (working with the hand dealt) that if I have just one list and pick from that list what I need depending on a click, that would be the route to go. I can do that successfully. It's just hooking it up to the slider that's not working.
Let me know if I need to provide further details.
Thanks.
Stephen

I am not familiar with the grid-slider plugin. But have you tried completely destroying the container DOM and then re-creating and inserting it. Then call the .gridSlider function again on the newly created DOM?

Related

An AJAX Search Plugin is removing my Event Listeners from an 'Accordion' style list

I'm using Search & Filter pro WP plugin for the ease of a client using it.
I've created a results page and filter on a demo site (for testing) that works fine but I know the categories will get large on the real site. So I turned the plugins' filters into an Accordion style list.
It works fine until certain searches reload all those filter results with AJAX and they remove my event listeners (which are sitting on elements for the moment, I know it's not ideal but for now I just want to see if it could work).
I imagine because my script has already been parsed when the DOM loaded, the AJAX from the plugin is just redefining those elements and they are then missing the Event Listeners or something.
Any help would be appreciated.
Here's my script:
<?php
add_action( 'wp_footer', function () { ?>
<script>
const clicker = document.querySelectorAll('#search-filter-form-4346 > ul > li > h4');
// looping through the <h4> elements and adding an event listener onto each, the class toggle just adds an animation to a pseudo-element spinner
for (let i = 0; i < clicker.length; i++) {
clicker[i].addEventListener("click", function() {
this.classList.toggle("open-filter-dropdown");
console.log('EL was created');
// declaring the <ul> as a variable
const openFilterPanel = this.nextElementSibling;
// animating the <ul> elements max-height
if (openFilterPanel.style.maxHeight) {
openFilterPanel.style.maxHeight = null;
} else {
openFilterPanel.style.maxHeight = openFilterPanel.scrollHeight + "px";
}
console.log('openFilterPanel style is changed');
});
}
</script>
<?php } );
I'm pretty new to javascript, I get the basic concepts but this kind of an interference is above my head. I tried refactoring my code, forcing the page to refresh and other such measures. None of these work very well. I also thought I could use a 'loadend' event on the document to re-add my ELs but that didn't work either.
Hoping there is a workaround here, otherwise I might have to find another solution or plugin.
Thanks in advance!

How does event bubbling/ capturing affect e.preventDefault()?

edit: For new viewers of the question, my title is misleading. The solution to this problem had nothing to do with event bubbling/capturing as was my assumption at the time of posting.
I have a mobile navigation menu that contains 3 <li> elements which, when clicked, toggleSlide() a dropdown menu. The jQuery code for each of them is largely the same:
const coreSlider = function() {
const aContainer = document.querySelector('#dropdown-A');
const aLinks = document.querySelector('#A-links');
const dontOpen = document.querySelector('.mob-prev-def');
$(aContainer).on('click', function() {
$(aLinks).slideToggle(200);
});
$(dontOpen).on('click', function(e) {
e.preventDefault();
});
}
...
Above is an example of the jQuery code on 1 of these dropdown containers, and behaves exactly as I want it to (toggles the drop-down menu on click, but does not redirect to the href value inside the <li> inside the container. However, I still want the links that appear on click to direct them to the relevant part of my website.
There are 2 more containers with jQuery code exactly like this, but for whatever reason the e.preventDefault() part of the code on them doesn't work as intended, and redirects on click. I figure that event bubbling/ capturing has something to do with it, but I'm not sure how exactly it is functioning in this example. To that end, what's going on here and how can I fix it to ensure all containers behave as I want? Here's my HTML:
<ul id="nav-links">
<li id="dropdown-A">
...
<div id="A-links">
...
...
...
</div>
</li>
<li id="dropdown-D">
...
<div id="D-links">
...
...
...
...
</div>
</li>
<li id="dropdown-E">
...
<div id="E-links">
...
...
</div>
</li>
<li id="gallery">Gallery</li>
</ul>
Edit: thanks to user Stone3m, I managed to add in a solution:
const prevDef = function() {
const dontOpen = $('.mob-pref-dev');
$(dontOpen).on('click', function(e) {
e.preventDefault();
});
}
prevDef();
The issue here is that you are using querySelector, which only selects one occurrence.
You should be using querySelectorAll with a forEach loop for every occurrence.
A quick and simple method would be to:
document.querySelectorAll('.mob-prev-def').forEach(item => {
item.addEventListener('click', function(e) {
e.preventDefault()
})
})
Sorry, I prefer vanilla JavaScript, but you could figure it out in jQuery now (I hope!)

Click all links on page containing string?

I'm trying to add a function to a toolbar Chrome extension I've made. What I'd like it to do is once a navigate to a particular page I'd like be able to push a button on the tool bar and have it "click" all of the links on that page containing harvest.game?user=123456 with 123456 being a different number for all of the links. It could be using jquery or javascript. The only catch is that the script will need to be inserted as an element to the head of the page as cross domain scripting is not allowed so no external references to a js file. I can handle the adding of the element but, I'm having no luck figuring out the actual function.
The elements containing the links all look like this:
<div class="friendWrap linkPanel">
<h5>Christine...</h5>
<div class="friend-icon">
<img src="https://graph.facebook.com/100001726475148/picture"></div>
<div class="levelBlock friend-info">
<p>level</p>
<h3 class="level">8</h3></div>
Harvest
<a class="boxLink" href="profile.game?user_id=701240"><span></span></a></div>
Something like this (I know this is a mess and doesn't work)? OR maybe something BETTER using jquery?
var rlr=1;
function harvestall(){var frt,rm,r,rld,tag,rl;
var frt=1000;
r=document.getElementsByClassName("friendWrap linkPanel");
rl=r.length;
rld=rl-rlr;
if(rld>=0){tag=r[rld].getElementsByTagName('a');
if (rl>=1 {rlr++;harvestall();}
else if (rl>=1) {tag[1].onclick();do something??? ;}
}
Something like this should work
$("a[href*='harvest.game?user=']").trigger("click");
// Using jQuery, wait for DOMReady ...
$(function harvestLinks() {
// Only create regexp once ...
var reURL = /harvest.game\?user=/,
// Create a ref variable for harvest links ...
// Use 'links' later without querying the DOM again.
links = $("a").filter(
function() {
// Only click on links matching the harvest URL ...
return this.href && reURL.test(this.href);
}
).click();
});

How to do backbone image slider

I am learning backbone and am not aware much in backbone.I need to do image slider using backbone.Though I can do using jquery like this
The link is the demo as per my requirement.There are in total 3 images.When you click on the last image then 1st two images disappears and new 2 images appear.
Can anybody please guide me how to do similarly using backbone.
This is my code using jquery
<img id="image-1" src="http://lorempixel.com/150/100/abstract">
<img id="image-2" src="http://lorempixel.com/150/100/food">
<img id="arrow" src="http://placehold.it/50x100">
$('#arrow').click(function() {
$('#image-1').attr('src', 'http://lorempixel.com/150/100/city');
$('#image-2').attr('src', 'http://lorempixel.com/150/100/sports');
});
Any help will be upvoted
What you are looking for are Backbone Views.
I personally really like the TodoMVC example, which is complete introduction to Backbone and its different components.
What you will need to do is to first wrap your content into a identifiable div, something like:
<div id="slideshow">
<ul class="images">
<li><img src="http://lorempixel.com/150/100/abstract" /><li>
<li><img src="http://lorempixel.com/150/100/food" /><li>
</ul>
<a class="next" href="#">Next</a>
</div>
Note that:
I only use an ID for the wrapper, not inside of it. It's preferable since backbone views have nice mechanism to work only with its own content (look at events and selector).
I wrap images inside a <ul>. Again, only in the purpose of making the structure more meaningful and querying easier.
Then, your view code should look like:
var MySuperView = Backbone.View.extend({
events: {
"click .next": "next"
},
interval: 1000,
initialize: function() {
// this is like a constructor
var _this = this;
setTimeout(function() {
_this.next(); // calling next() every `interval` ms
}, _this.interval);
},
next: function() {
// here goes your logic
return false;
}
});
And finally, to bind the view to the matching element:
var view = new MySuperView();
view.setElement(document.getElementById("slideshow"));
And voilĂ  :)

Initializing a jquery mobile widget on a page gives "Cannot read property 'jQuery....' of undefined"

Fiddle Example
The site has 2 (or more) pages defined in HTML like this:
<div id="page1" data-role="page">
<div data-role="content" id="page1-content">
Next Page
</div>
</div>
<div id="page2" data-role="page">
<div data-role="content" id="page2-content">
Go Back
</div>
</div>
In Javascript - I am at once initializing everything:
$(function(){
$('#page1-content, #page2-content').each(function(){
var ul = $('<ul>');
$.each(['one','two','three'], function(item){
ul.append('<li>'+item+'</li>');
});
ul.appendTo(this).listview();
});
});
But the page only initializes the list view on the 1st page, and the list view on the 2nd (and not currently visible) page does not initialize and gives me an error.
Cannot read property 'jQuery19107783124386332929' of undefined
What am I doing wrong?
I really want to be able to start fetching the data and at least create the DOM in every page at once in the beginning, rather than waiting till the user clicks "Next Page".
Also - the list view on the first page is overlapping the "Next" button. I see this overlapping often and would like to fix/understand this too.
Page data-role=page in jQuery Mobile passes through different stages, as shown in the diagram below.
Image / diagram source: http://bradbroulik.blogspot.co.nz/2011/12/jquery-mobile-events-diagram.html
Enhancing widgets manually should be called on active page only, otherwise it will result in a error.
To do add fresh elements on different pages, you need to do this when pagecreate or pagebeforecreate events occur, without the need to call any enhancement method. As the widget will be auto-initialized/enhanced during that stage.
Also, you have a mistake in your code where you didn't close ul tag. However, this didn't cause the error.
The below code shows how to add elements to different pages without the need to call any enhancement method manually.
$(document).on("pagecreate", "[data-role=page]", function (e) {
var ul = $('<ul data-role="listview" data-inset="true"></ul>'),
html = '';
$.each(['one', 'two', 'three'], function (item) {
html += '<li>' + item + '</li>';
});
ul.append(html);
$('[data-role=content]', this).append(ul);
});
Demo
This might be a particular issue of the versions combination you're using, but before checking that, I would try delegating on the 'pageinit' event instead of the regular document ready.
Please try this:
$(document).on('pageinit',function(){
$('#page1-content, #page2-content').each(function(){
var ul = $('<ul>');
$.each(['one','two','three'], function(item){
ul.append('<li>'+item+'</li>');
});
ul.appendTo(this).listview();
});
});

Categories