I have photo gallery type of web page. It is comprised of grid of photo thumbnails, with assigned tags beneath them.
At top of page is a list of all of the tags, formatted as buttons. User can click on one or more tag buttons to filter photos. I am using Isotope js for this filtering. This works fine.
However I want to add new feature that runs after the Isotope filtering that will hide any tag buttons that are not assigned to any of the remaining filtered photos.
My plan was to do following:
identify all visible photos
create array from visible photos' tags
use array to hide any unmatched tags
However, I am having problems identifying visible photos after I click tag buttons and Isotope does its filtering.
It seemed like it would be quite straightforward. Isotope js changes a specified element's display = "none" for the hidden photos. Eg if I inspect the element, I can see hidden elements have display = "none"
I created a 'visibletags' function that is called at end of tag button on click to find elements where display != "none":
$('.filter-button').on('click', function() {
// isotope code here
// isotope hides some photos setting their div's `display = "none"`
visibletags();
}
function visibletags(){
var imgdivs = document.querySelectorAll('.imgdiv');
var showtags = [];
for (var i=0, max=imgdivs.length; i < max; i++) {
if (window.getComputedStyle(imgdivs[i]).display != "none") {
// this should be a div that is not hidden by Isotope js
// so loop through it's tags to build array
// array will be used later to hide unmatched tags
var phototagspans = imgdivs[i].querySelectorAll('.photo-tag');
for (var j=0, max=phototagspans.length; j < max; j++) {
showtags.push(phototagspans[j].className);
}
}
}
}
But I am not able to identify the element's display value. I have tried using window.getComputedStyle(imgdivs[i]).display, imgdivs[i].display and imgdivs[i].style.display
Edited to modify question:
I tested running the 'visibletags()' function as another button on click event that I manually click after the isotope filtering is complete. This successfully gets all photo element display values. So that part of code does work.
So my question now is how to run 'visibletags()' function after the Isotope filtering in a way that it is one complete set of code?
I tried changing the 'visibletags()' function to run as $('.filter-button').mouseup(function() but didn't get filtering results.
Maybe this is now general Javascript question eg how to get on click event's results after they have been rendered?
I'm not familiar with Isotope, but you probably need to use a callback function. Check out this documentation, especially the layoutComplete part: https://isotope.metafizzy.co/events.html
In your case, the result could be something like this:
$('.filter-button').on('click', function() {
// initialize isotope
$isotope.on('layoutComplete', function() {
visibletags();
});
// other isotope code here
}
Related
I am trying to add items into a div by appending to the end of the div during a for loop. The idea being I have records from a database with a "Total score" field, and I want the highest-scoring records on top.
The container div where I want to put the elements is this:
<div id="fetch-n-sites-output"></div>
And my callback function upon a successful AJAX call is as follows:
function updateNSites(data){
$("#fetch-n-sites-output").empty();
data["fetched-sites"].map(function(element, index){
var cloned = cloneTemplate(index, element);
console.log(`Processing site ${index}...`);
$("#fetch-n-sites-output").append(cloned);
});
}
For completeness, my cloneTemplate function is this:
function cloneTemplate(index, data){
// CLONE TEMPLATE
var template = $("#test-template").html().trim();
var clone = $(template);
var siteID = data["site_id"];
var totalSiteScore = data["total_site_score"];
// UPDATE CLONE WITH SITE-SPECIFIC INFORMATION
// 0. Update ID of div element with site id
var mainDiv = $("div.greendiv").eq(index);
mainDiv.attr("id", `site-${siteID}`);
// 1. Update header with "Site ID: {site_id}"
$(`#site-${siteID} > div.reddiv > p.header-text`).text(`Site ID: ${siteID} - Total Score: ${totalSiteScore}`);
// 2. Add event handlers
// $(`#site-${siteID} div.site-sidenav`).on("click", 'a', function() {
// alert("clicky");
// });
$(`#site-${siteID}`).find(".site-navlink").on("click", function () {
alert("clicky");
});
// SHOW CLONE
clone.removeClass("template-hidden").addClass("template-show");
return clone;
}
When I retrieve 3 sites, the highest scoring site is always at the bottom. This doesn't make sense to me, because my SQL query orders by this total site score. The highest scoring site is always placed at the bottom, but the other sites are ordered properly:
Another odd problem (which I think is related to this main issue) is that the event handlers that I add in the cloneTemplate function to handle clicks on the <a> tags (right now just calls an alert() for testing) only work on the elements other than the first:
Whereas clicking 'GENERAL' on the first element does not trigger an alert. Could someone please help me figure out why every added element but the first behaves properly, but the first does not? And why the first element is always getting placed at the bottom? Thank you
I'm working off of a tutorial from codrops. There is a hover event for each item, as well as a click event that triggers an anime.js function.
I'm trying to work this so certain items (grid cells) don't trigger the anime.js function when clicked, but the hover function still works.
I've tried simple css pointer-events, but that disables the hover function.
I've constructing the two groups as separate items in JS, but then the animation doesn't work the same (it staggers the two different classes).
I've tried things to stop the default javascript behavior, but it seems to have no impact on the code.
Help!!!
I've made a functioning codepen - in the option there I'm trying to disable click event for any grid item with the id="noClick" - to no avail.
$('noClick').observe('click', function(event) {
Event.stop(event);
});
This is the primary function that creates the event
this.DOM.items.forEach((item, pos) => {
// The item's title.
const title = item.dataset.title;
// Show the title next to the cursor.
item.addEventListener('mouseenter', () => cursor.setTitle(title));
item.addEventListener('click', () => {
// Position of the clicked item
this.pos = pos;
this.title = title;
// Start the effect and show the content behind
this.showContent();
// Force to show the title next to the cursor (it might not update because of the grid animation - the item under the mouse can be a different one than the one the user moved the mouse to)
cursor.setTitle(title);
});
});
where 'item' is
this.DOM.grid = this.DOM.el.querySelector('.grid');
// Thr grid items
this.DOM.items = [...this.DOM.grid.children];
// totla number of grid items
this.itemsTotal = this.DOM.items.length;
I've tried to create multiple items
this.DOM.grid = this.DOM.el.querySelector('.grid');
this.DOM.yesClick = this.DOM.el.querySelector('.yes-click');
this.DOM.yesClickTwo = this.DOM.el.querySelector('.yes-click-2');
this.DOM.noClick = this.DOM.el.querySelector('.no-click');
// Thr grid items
this.DOM.items = [...this.DOM.yesClick.children, ...this.DOM.yesClickTwo.children];
this.DOM.itemsNo = [...this.DOM.noClick.children];
this.DOM.allItems = [...this.DOM.noClick.children, ...this.DOM.yesClick.children, ...this.DOM.yesClickTwo.children];
// totla number of grid items
this.itemsTotal = this.DOM.allItems.length;
This works, but messes with the animaton.
Here is the codepen
I feel this is really simple and I'm missing something. Looking to learn, so a push in the right direction or any help would be greatly appreciated!
1. You have multiple elements with the same ID. But ID attribute must be unique.
2. You used $('noClick'), but ID selector would look like #noClick
If you want to mark few elements, use a class and select them like .elementclass. It is possible for element to have multiple classes, separated by space.
Your selector doesn't seem correct so you either need #noClick or .noClick as the selector however you can stop the javascript from bubbling like this :-
$(".noClick").click(function(e) {
// Do something?
e.stopPropagation();
});
I have a table with some items and those items can be selected by adding a tick. Check the image attached:
What I need to achieve is to hide the row which does not contain any ticks to be not visible. This is because in my app I have to generate lists of the items contains only ticks in another view. So when I will press the generate button that row will be hidden.
I just want to say if that row does not contain any 'glyphicon-ok' need to be deleted/hidden when I will generate the view with the list of those items.
I tried something like this:
SveCrf.prototype.hideRowWhereNoTicksForm = function () {
var tr = document.getElementsByTagName('tr');
for (var i = 0; i < tr.length; i++) {
switch (tr.item(i).getElementsByTagName('td').item(0).className) {
case "glyphicon-ok":
tr.item(i).style.display = "none";
break;
}
}
}
This doesn't do anything.
I would like to see an example of being able to resolve this issue.
Correct me if I'm wrong but you don't seem to have provided HTML you want to act upon but just a screenshot and a link to some RoR code in the comments that generates the HTML. Also you don't show how you try to execute SveCrf.prototype.hideRowWhereNoTicksForm, and furthermore I'm not really sure at all what you are trying to do with switch/case (I also don't understand what item is supposed to be; this is where providing us with actual HTML might have helped).
In addition, as I've alluded to in some comments of mine, you are really trying to do two things. I don't know if you've seen this Stackoverflow page yet about creating "a Minimal, Complete, and Verifiable example" but I think reviewing that will help improve your StackOverflow experience moving forward (and also for me it validated my suggestion of "divide and conquer").
All of which I think made it hard for you to get the help you desired. In any case below I'm providing some sample HTML with a table containing four rows total, two with a cell that contains the class foo, and two that don't. Beneath that is my non-jQuery code selecting the rows with no cells containing the class foo, and then hiding them; furthermore there is a demo of the same functionality using jQuery at https://repl.it/#dexygen/HideRowsWithNoCellsWithClass
<table border="1">
<tr><td class='foo'>foo</td><td></td><td></td></tr>
<tr><td></td><td>bar</td><td></td></tr>
<tr><td></td><td></td><td>baz</td></tr>
<tr><td class="foo">foo</td><td>bar</td><td>baz</td></tr>
</table>
/*
We cannot call `filter` directly on an HTMLCollection such as returned by
"document.getElementsByTagName('tr')" as it is not a bona fide array, so we use
"[].filter.call()", and we return only those rows that *fail* the test
"row.querySelector('td.foo')", then we loop over these with `forEach` and hide them
*/
[].filter.call(document.getElementsByTagName('tr'), function(row) {
return !row.querySelector('td.foo');
}).forEach(function(row) {row.style.display = 'none'});
A number is generated on my page in a hidden text field based on a number of 'pages' that need to be produced.
The plan is to dynamically allow the user to switch between the pages by clicking on numbers (e.g. if we have 3 pages, they can click on 1, 2 or 3 to display each page).
The problem is that the number of pages will vary from run to run, but in order for me to be able to add the page switching functionality in js/jQuery, I need to create a .click() method for each page button.
However because the number of pages isn't a set amount, I need to somehow create these methods dynamically - creating as many as are required, so a .click() function for each page.
$("#page1" ).click(function() {
for(var i = 0; i < tableCount; i++){
$("#usertable" + (i)).hide();
}
$("#usertable1").show();
});
Above is an example of a simple function I wrote that will be executed when the 1st page button is pressed and it iterates through all pages, hides them and then shows the 1st page. However if I have 5 pages, I need to somehow dynamically create a $("#page2" ).click() function and a $("#page3" ).click() function and so on.
Any ideas?
You can use attribute begins with selector
$("[id^=page]").click(function () {
// do stuff
var id = this.id.slice(-1);
for (var i = 0; i < tableCount; i++){
$("#usertable" + i).not("[id$=" id "]").hide();
}
$("#usertable" + id).show();
})
Use of common classes and some attributes will help
<a class="page-link" href="#page1">Page 1</a>
<div id="page1" class="page-content"></div>
Then one click handler works for whole class of links. Within any jQuery event handler this is the element the event occured on
$('.page-link').click(function(){
// hide whole class of content , filter for the one to show
$('.page-content').hide().filter(this.hash).show();
});
Or a bit more verbose for understanding
$('.page-link').click(function(){
var idSelector = $(this).attr('href'); // "#page1"
$('.page-content').hide().filter(idSelector).show();
});
I've got a modal window. What I want to happen is to remove certain elements from the page when the modal opens and add them back in right where they were after the modal closes. I don't want to do display:none, because that only hides them, I need them to actually be removed from the page. So I have a bit of jQuery to remove and add them back in after a timer just for testing...
UPDATED: With these additions to the code, it now grabs the element before, then adds it back in after that same element. The issue is, what if that element was also removed? Then it won't add back in! Also, won't javascript event handlers be lost in this? I'm developign a plugin, so it should interfere with the site as little as possibl,e but 3d elements have a bug in them with Safari that is impossible to get around.
Any ideas on how I could temporarily remove 3d elements without interfering with people's site too much?
$3delements = $('*').filter(function(){return $(this).css('-webkit-transform-style') == 'preserve-3d'});
$3delementsposition = $3delements.prev()
//On modal open
$3delements.remove();
//On modal close
$3delementsposition.after($3delements);
The problem is that this requires I specify a certain place in the DOM for them to come back in. I'd like the elements to come back in where they were. How can I make sure the elements don't change/move/lost information on the .remove to the .append.
Use .detach() and .append() to remove and reattach elements, it will maintain all your events and data.
If you add elements back in the reverse order that you removed them, they should all fall back in place
untested code
var elems3d = $(...);
var elemsRemoved = [];
// removing
elems3d.each(function(i,o) {
var elem = $(o);
elemsRemoved.push({
loc: elem.prev(),
obj: elem.detach()
});
});
// adding back
while (elemsRemoved.length) {
var elem = elemsRemoved.pop();
elem.loc.after(elem.obj);
}
Instead of removing the elements, replace them with placeholder elements (using replaceWith) then replace the placeholders with the original content when needed. Something like the following:
$3delements = $('*').filter(function(){return $(this).css('-webkit-transform-style') == 'preserve-3d'});
var originals = [];
$3delements.each(function() {
// Clone original, keeping event handlers and any children elements
originals.push($(this).clone(true));
// Create placeholder for original content
$(this).replaceWith('<div id="original_' + originals.length + '"></div>');
});
///
/// Do something asynchronous
///
// Replace placeholders with original content
for (var i in originals) {
$('#original_' + (i + 1)).replaceWith(originals[i]);
}
See clone and replaceWith in the jQuery docs for more info.
I have created the fiddle. Let me know if this fulfills your requirement.
http://jsfiddle.net/mNsfL/12/