jQuery selector not behaving as expected in parsed HTML from ajax - javascript

I've ran into a weird issue today, I'm hoping someone else can help me figure this out.
The project that I'm working on is more-or-less a jQuery slideshow. I have a super simple file that I'm loading to test everything out, it looks something like this:
<!doctype html public "(╯°□°)╯︵ ┻━┻">
<html>
<head>
<meta charset="utf-8">
<title>test</title>
</head>
<body>
<div id="slides" data-slidesShow="holder">
<div class="slide" id="test1">test div 1</div>
<div class="slide" id="test2">test div 2</div>
<div class="slide" id="test3">test div 3</div>
</div>
<button id="previous">previous</button>
<button id="next">next</button>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="js/libs/jquery-1.7.2.min.js"><\/script>')</script>
<script type="text/javascript" src="js/slides.js"></script>
<?php include 'footer.php'; ?>
</body>
</html>
Again, nothing even remotely fancy here.
Now, in jQuery I'm getting and parsing the page like:
$.ajax({
url: target.path,
dataType: "html",
complete: function(data){
var $slides = $('[data-slidesShow="holder"]', $(data.responseText));
console.log($slides); // returns []
}
});
BUT! $slides returns an empty array, unless I wrap it in a meaningless div, like:
<div class="stupid-wraper-div-that-i-dont-want-or-need">
<div id="slides" data-slidesShow="holder">
<div class="slide" id="test1">test div 1</div>
<div class="slide" id="test2">test div 2</div>
<div class="slide" id="test3">test div 3</div>
</div>
</div>
and now:
console.log($slides); // returns [<div id="slides" data-slideShow="holder">...</div>]
I've read over the jQuery docs on this (http://api.jquery.com/jQuery/) and other StackOverflow conversations, but none of them explain why I would need a wrapper div to have results returned.
Any ideas? I know it's not a huge issue, but I don't want to have to hack a fix when I could find the root of the problem.
...
TL;DR: jQuery's select in scope only works with a weird wrapper div

When you pass a context to $(), you're asking the selector to look among its descendants for the given element (this works like .find()). Without your wrapper div, the element you're looking for is the context element, so your selector won't be able to find it since it's looking inside it instead.
You should use .filter() instead, which filters a set of elements instead of searching their descendants:
var $slides = $(data.responseText).filter('[data-slidesShow="holder"]');

You need to use filter.
$(data.responseText).filter('[data-slidesShow="holder"]')
The html and body tags get stripped by the browser leaving their content.

Related

Remove all events from HTML Element and its child using TypeScript

I have unknown HTML that may be changed each time page load, that it is being retrieved from an API.
How can i remove all events from it ?
html :
<div id="content">
<h2 onclick="alert('hi');">Test 1</h2>
<div onmouseover="alert('hi')">Test 2</div>
</div>
I want it to be
<div id="content">
<h2>Test 1</h2>
<div>Test 2</div>
</div>
I already searched and found a solution but it doesn't work for me
var el = document.getElementById('content'),
elClone = el.cloneNode(true);
el.parentNode.replaceChild(elClone, el);
the elClone element still contains events. How can i remove all events from a given html block?
You can remove on attributes with something like this:
function removeOnAttributes(element)
{
for (var attribute of element.attributes)
if (/^on/i.test(attribute.name))
element.removeAttribute(attribute.name);
for (var element of element.children)
removeOnAttributes(element);
}
removeOnAttributes( content );
content.outerHTML = content.outerHTML; // to remove the other non-atribute event handlers
console.log( content.outerHTML );
<div id="content">
<h2 onclick="alert('hi');">Test 1</h2>
<div onmouseover="alert('hi')">Test 2</div>
</div>
So, if I understand your question right, you want a function in your JavaScript to turn this:
<div id="content">
<h2 onclick="alert('hi');">Test 1</h2>
<div onmouseover="alert('hi')">Test 2</div>
</div>
Into this:
<div id="content">
<h2>Test 1</h2>
<div>Test 2</div>
</div>
All you have to do is this:
First add IDs to your elements (e.g. id="test1" and id="test2")
function funcname() {
document.getElementById("test1").removeAttribute("onclick");
document.getELementById("test2").removeAttribute("onmouseover");
}
Then call your function when you want to, and you're set!
Even if you don't want to do it the way I did, all you need is the removeAttribute(); function.
You said the response came from an API and you append it to the html.
You can have a look at the CSP header it might suit your case, you can entirely remove inline scripts.
Either way, there are also parsers that can do that for you I believe.
Take a look at https://code.google.com/archive/p/google-caja/wikis/JsHtmlSanitizer.wiki

Selecting only one div of entire css class in jQuery

my goal is to show an overlay on a div when that div is hovered on. The normal div is called .circleBase.type1 and the overlay is circleBase.overlay. I have multiple of these divs on my page. When I hover over one .cirlceBase.type1, overlays show on every .circleBase.type1. How do I prevent this?
Here is some code:
HTML
<div class="circleBase type1">
<p class="hidetext">Lorem ipsum</p>
<hr size="10">
<strong class="gray hidetext">gdroel</strong>
</div>
<div class="circleBase overlay">
<p class="date">11/12/14</p>
</div>
and jQuery
$(document).ready(function(){
$('.overlay').hide();
$('.date').hide();
$(".circleBase.type1").mouseenter(function(){
$(".overlay").fadeIn("fast");
$('.date').show();
$('.hidetext').hide();
});
$(".overlay").mouseleave(function(){
$(this).fadeOut("fast");
$('.date').hide();
$('.hidetext').show();
});
});
Use $(this) to get current element reference and do like this:
$(".circleBase.type1").mouseenter(function(){
$(this).next(".overlay").fadeIn("fast");
$(this).next(".overlay").find('.date').show();
$(this).find('.hidetext').hide();
});
and:
$(".overlay").mouseleave(function(){
$(this).fadeOut("fast");
$(this).find('.date').hide();
$(this).prev(".circleBase").find('.hidetext').show();
});
usually when I want to target something specific you just give it an ID.
ID's play better in JavaScript than classes.
If you had a specific container, using the container as your starting point is a good route as well
$('#container').find('.something.type1').doSomething();
This is much more efficient for jquery, because it only searches .something.type1 inside of #container.
Well I'm not sure exactly what you're looking to do, but it looks like you want to replace content in some kind of circle with a hover text, but with a fade. To do that you'll have to add some CSS and it would be best to change your HTML structure too.
The HTML should look like this:
<div class="circleContainer">
<div class="circleBase">
<p>Lorem ipsum</p>
<hr>
<strong class="gray">gdroel</strong>
</div>
<div class="overlay" style="display: none;">
<p class="date">11/12/14</p>
</div>
</div>
so your js can look like this:
$(function(){
$(".circleContainer").mouseenter(function(){
$(this).find(".overlay")
$(this).find('.circleBase').hide();
});
$(".circleContainer").mouseleave(function(){
$(this).find('.circleBase').show();
$(this).find(".overlay").hide();
});
});
Here's a working solution that includes some CSS to make it nice. Try taking it out and running it, you'll see the problems right away.

Accessing DOM elements within a Handlebar template

This must be simpler than I'm making it out to be. Not sure what's going on.
I have a DIV that I'm "filling" with a Handlebar template. Once the template is generated, I use a jQuery slideDown to open the panel to view the contents. Now I need to put a close function to slideUp the DIV.
I think the problem is the click function isn't getting bound because the a.close element is within a script tag.
Here's the DIV for the content:
<div id="characteristic" style="bottom:0px; width:800px; display:none; position:fixed; left: 350px;"></div>
Here's the jQuery snippet. This is at the top of the HTML:
$(document).ready(function(e){
$("a.close").click(function(e) {
e.preventDefault();
$("#characteristic").slideUp();
});
});
And a snippet of the template:
<script id="ac-template" type="text/x-handlebars-template">
<div class="holder" style="background-color:#FFFFFF;">
<div class="frame">
<div class="content">
<div class="info-box-holder">
<a class="close" href="">×</a>
<div class="heading">
<h2>ACTIONABLE CHARACTERISTIC</h2>
</div>
<div class="info-box">
<img class="alignleft" src="{{image_large}}" alt="" width="400" height="400" />
{{#if subcategory_name}}
<h2>{{subcategory_name}}: {{name}}</h2>
{{else}}
<h2>{{category_name}}: {{name}}</h2>
{{/if}}
I know this is an old question and you've probably already worked out the answer, but yes, it's because at the time that your JS code runs, a.close does not exist in the DOM.
You need to either run the JS code after handlebars has finished rendering the template, or bind to a higher level DOM element that exists on page load (a container of some sort) and then activate only for the link that you want. Something like this (see the API):
$(document).ready(function(e){
$("#mycontainerdiv").on('click', 'div.info-box-holder a.close', function(e) {
e.preventDefault();
$("#characteristic").slideUp();
});
});

jquery double function each

I have the following block of HTML code more than once
<div id="page_1" class="page">
<div class="imageDetail_bg">
<img src="../_img/detail_car.jpg" alt="" id="car_detail" class="car_detail"/>
</div><!-- imageDetail-->
<div id="listThumbs">
<div id="thumbsContainer_1" class="thumbsContainer">
<div id="areaThumb" class="areaThumb">
<div id="posThumb_1" class="posThumb">
<img src="../_img/detail_car.jpg" class="detail_img" alt="">
</div>
</div><!--areaThumb-->
<div id="areaThumb" class="areaThumb">
<div id="posThumb_2" class="posThumb">
<img src="../_img/detail_car.jpg" class="detail_img" alt="" />
</div>
</div><!--areaThumb-->
...
...
...
</div><!--listThumbs-->
</div><!--page-->
and the following jQuery code:
$('.page').each(function(i) {
$('.areaThumb').each(function(j) {
$('.detail_img').eq(j).click(function(){
$('.car_detail').eq(i).attr('src', $(this).attr('src'));
});
});
});
What I want to do is: For each page there's a block of thumbs, and when I click in any thumb, the image in #car_detail is replaced by the image of the thumb I clicked. At this moment I can do this, BUT the #car_detail image is replaced in all pages. I'm not getting individually actions for each page. Every click make the action occurs in all pages.
Can anyone tell me what I'm doing wrong?
Thanks
You need not iterate through each element of the jquery selector result to bind a click event.
And you are missing a closing div for thumbsContainer div, add that before each .
Also if you have an element with id car_detail then you should use #car_detail instead of .car_detail
Working example # http://jsfiddle.net/2ZQ6b/
Try this:
$(".page .areaThumb .detail_img").click(function(){
$(this).closest("div.page").find('.car_detail').attr("src", this.src);
});
If the .detail_img elements are being used for the car_detail image then you can simplify the above code to:
$(".detail_img").click(function(){
$(this).closest("div.page").find('.car_detail').attr("src", this.src);
});
You need to give context to your children nodes:
$('.page').each(function(i) {
$('.areaThumb', this).each(function(j) {
$('.detail_img', this).eq(j).click(function(){
$('.car_detail', this).eq(i).attr('src', $(this).attr('src'));
});
});
});
Every this is pointing to the current element given by the jquery function that called it.
[edit] Cybernate found a better way to do what you wanted to. My answer mostly explains why your code did not work as you wanted
I think you have the wrong approach about this,
You should just use cloning and you will be fine...
HTML
<div class="holder">Replace Me</div>
<div>
<div class="car"><img src="img1" /></div>
<div class="car"><img src="img2" /></div>
</div>
JS
$('.car').click(function(){//when you click the .car div or <img/>
var get_car = $(this).clone();//copy .car and clone it and it's children
$('.holder').html('').append(get_car);//put the clone to the holder div...
});
I think this is what you should be doing, simple and elegant... do not understand why you complicate as much :)

How to add html after a certain div using Prototype JS?

I want to be able to append divs to my page such that they are appended after a div of a certain class and before teh divs that follow it i.e:
<div class="header">Header DIV</div>
<!-- Want to add using javascript some HTML right here-->
<div class="one-basic-div">...</div>
<div class="one-basic-div">...</div>
<div class="one-basic-div">...</div>
<div class="one-basic-div">...</div>
It is basically raw html I wish to add. How can I do it?
Use the Element.insert method:
document.observe("dom:loaded", function() {
$$(".header").first().insert({ after: "<p>Some html</p>" });
});
<script src="https://ajax.googleapis.com/ajax/libs/prototype/1/prototype.js"></script>
<div class="header">Header div</div>
<div class="one-basic-div">Other div</div>
Use insert:
$$('#header').insert({ 'after' : theHTML });
That should insert it as a sibling after the div with id header.
There are some helpful examples here, the documentation seems to be lacking, somewhat.

Categories