Hy, I'm having some problems making lightbox (in my case slimbox 2) work
after new content is loaded. I understand that slimbox needs to be called again, but I tried almost everything.
Here is the code how new content is been loaded:
...var link = $('<a class="loadpost" href="javascript:">Load more</a>');
link.click(loadMore);...
loadMore is the function that loads new content. this is just a piece of code. if you need the whole code let me know.
Here is the slimbox code.
jQuery(function($) {
$("a[rel^='lightbox']").slimbox({/* Put custom options here */}, null, function(el) {
return (this == el) || ((this.rel.length > 8) && (this.rel == el.rel));
});
the new content has the rel attributes but it wont work. can i combine the click function from above to call slimbox code again.
Long version:
Without more of the code (if you can, make a jsfiddle or something similar) it's hard to know if this is the only issue, but one immediate issue I'm seeing is that you're using jQuery's $(" ") wrong. You're supposed to put a selector in there, so that jQuery can find whatever it is you want it to find in the DOM; what you're feeding it is a string of HTML.
Browsers use HTML to make the DOM (Document Object Model) tree, which you can think of as an abstracted logic tree of your HTML; the DOM sees 'a.loadpost' as a parent of the text node inside of it (in this case, 'Load more'). CSS and Javascript/jQuery both find information from that abstracted logic tree in pretty similar ways. With a few exceptions, if you know how to target something with CSS, you know how to target it with jQuery -- just select whatever HTML you need to select the same way you would with CSS.
So, to tell jQuery to do something to a link with a class of 'loadpost', you simply write $('a.loadpost'). $("a.loadpost") works as well.
Short version:
var link = $('a.loadmore');
Related
I'm trying to write some JavaScript that once the page has finished loading will create a div in the place where the is placed.
Here is a stripped-back version of the code...
window.addEventListener('load', function () {
var content = document.createElement('div');
content.id = 'div-ID';
document.getElementsByTagName('body')[0].appendChild(content);
});
It works outside of the addEventListener(), however, when inside the event listener it always puts the created div below the rest of the page content not in the place the <script> tag is placed.
I'm certain the issue is to do with this line...
document.getElementsByTagName('body')[0].appendChild(content);
I need an alternative version to this which doesn't appendChild() but my JS isn't that good and everything I've tried hasn't worked.
Its most likely simple to achieve, I've tried searching Google and Stack Overflow but my search terms don't seem to be producing the desired results.
Any help on this would be much appreciated
You could do it with Node.insertBefore
As such, your code would be something like:
document.body.insertBefore( content, document.body.childNodes[0] );
The second parameter is the referenceNode, that has following comment:
referenceNode is not an optional parameter -- you must explicitly pass a Node or null. Failing to provide it or passing invalid values may behave differently in different browser versions.
Inserting dynamic SVG content into the DOM does not work as expected, having the SVG element onload attribute (containing JavaScript) regarding: "setInterval()".
As noted in the search tags of this question; this is plain (valilla) JavaScript (not jQuery); here's a breakdown of the issue:
I have some SVG code (plain text) that gets inserted into a <div> as innerHTML
the SVG element has an onload attribute with some JavaScript inside it
the JavaScript contains setInterval(...) - (which does not work at all)
I grab the SVG element from the temporary div and return it as the result of a function.
this result is appended into some element in the live DOM (body)
the strange issue:
any other code inside that onload attribute works fine,
only setInterval & setTimeout is completely ignored
More info:
During runtime (start-up), the SVG code is grabbed from an existing embed element .getSVGDocument() (after it has loaded) and prepared as plain HTML which can just be used as a template to create many others from the same source-code. I'm not using cloneNode(true) -because: the interval is for animation (continuous slow & smooth rotation) - which could have a heavy impact on client-side resources, hence, I thought it best to grab the code and keep it as template - then remove the original from the DOM.
With all the above in mind, everything works fine:
The (new) SVG shows up on screen, all nice and dandy-like
When I console.log the (inline) SVG code that is used, all looks perfect
I get no errors, and there is no error handler that mutes errors (window.onerror == null)
The JavaScript (text) inside the SVG node's onload attribute works for things like: console.log(this) - (which produces the SVG element in the log) - however, as mentioned: setInterval() & setTimeout() is just simply ignored - without any error and no warning.
The following code is a very short example, and (regrettably) it works; however, in my production app it doesn't.
The code:
var html = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" onload="setInterval(function(){ console.log(\'testing\'); },500);">';
var temp = document.createElement('div'); temp.innerHTML = html;
var node = temp.getElementsByTagName('svg')[0];
document.body.appendChild(node);
If you test the above code in a new js file, it works; however, for the life of me I can't find the reason why it breaks in my app; as explained, it's quite simple really.
The question:
Does anyone know if there is some "gotcha" I'm not aware of regarding this? Maybe name-spacing?
If the source-code is required, I can load it up on JSFiddle, or CodePen -if required, but, it's a lot of code, and many files, which may not be necessary for publication.
I'm sure it's just something small; like, how timers register according to scope, and maybe how it's affected in .bind() ?
I'm really stuck with this, and I kinda need it working for a good impression for a job-interview; so if you know anything that could help, I would appreciate your input very much.
Thank you.
embedded content, onload attributes & the DOM
The following may help in related scenarios:
when targeting an asynchronous source, make sure the contentDocument or getSVGDocument() contains the resources you need to access. The window.onload, or the DOMContentLoaded event is relative to the current DOM, so it may help constructing your own listener->trigger for a cross-browser solution, because the contents you need may not be ready in a synchronous fashion.
the onload attribute/event is not triggered when inserting dynamic content that is not asynchronously loaded, but may fire under certain circumstances, so, again, a custom:
listen->trigger will solve that.
question specific
The question is directly related to the 2nd point above, and the answer is quite simple really:
in the "onload" attribute of said SVG, set a simple value as property of this like:
<svg onload="this.ready = true; setTinterval(...)"
in the constructor function, after the element was dynamically created, simply check if the svg-node's onload event was fired like so:
if (!svgNode.ready){ svgNode.onload(); }
If there is an error in your code, but no error is shown, make sure window.onerror is either null -or if it's a function, make sure it does NOT return true - else it may suppress errors and you'll have a hard time tracking down what's wrong.
Please feel free to either improve this answer, or comment and I'll improve it accordingly; however, better answers will be appreciated.
6 years later...
With vanilla JavaScript Web Components you can do:
<load-svg></load-svg>
<script>
customElements.define("load-svg", class extends HTMLElement {
connectedCallback() {
this.innerHTML = `<svg></svg>`;
setInterval(() => {
console.log("testing");
}, 500);
}
});
</script>
I have somewhere on website a specific text, let's say "NewYork", and I want to fire a Google Analytics event to track all the occurrences of this string.
For example if a visitor come to a webpage that contain the string 'NewYork', I want to send a Google Analytics event.
Text string is in a span tag as <span class="city">NewYork</span>
I do not know any JavaScript codes, just tried the following code adapted from someone else. And it is not working at all.
<script>
var htmlString = $('body').html().toString();
var index = htmlString.indexOf("NewYork");
if (index != -1)
{ ga('send', 'event', 'yesNewYork', 'foundnewyork'); } </script>
Does anybody know how to do this?
Possibly a jQuery solution?
Your approach is correct in principle, but can be improved upon.
You are already using jQuery syntax so I just assume jQuery is available.
First I would suggest you follow Lars Graubner suggestion and select a more specific element and grab the text content instead of the html (as the name suggests the text()-function does not return HTML markup but text only).
$('.city').text()
will adress the span from your example - the dot in the selector says "Look for elements with a classname of".
This will actually return all elements that have the class, but for your use case that does not matter much.
However you must make sure that the text is actually rendered on the page before you call your jQuery selector; if you place it in the head of the page the text isn't there yet when the event tracking runs and thus your selector will return nothing.
You can either put the script in the footer of the page, or you can wrap it into jQuery's document.ready-call. This makes sure that the function only runs after the DOM of the document has rendered (meaning that the page structure is complete, event if images and other assets are not yet loaded. Text will be present at this point). So this would look like this:
$( document ).ready(function() {
var myString = $('.city').text();
if(myString.indexof('NewYork') > -1) {
ga('send', 'event', 'yesNewYork', 'foundnewyork');
}
});
(Obviously this assumes you have jQuery included).
If this still doesn't work you need to be more specific as to the actual error your are getting.
My content is replaced with ajax but sometimes an element will have the same id on two pages (ie, a photo on the home page has the same id on the gallery page). This means that when dojo.parser.parse is called, the widgets are trying to be re-added, and the below error is thrown:
Error: Tried to register widget with id==____ but that id is already registered
Ideally, what I'd like to do is run destroyRecursive on the DOM node that the AJAX replaces. I've tried both of the below but neither work (I believe destroyRecursive is for widgets not DOM?):
dojo.byId('main').destroyRecursive();
dijit.byId('main').destroyRecursive();
Is there a good way of doing this, or do I need to try and ensure that all my id's are different?
You are on the right track, and you are correct that destroyRecursive only exists on widgets. However, there are a couple of choices to accomplish what you want to do.
If you're using widgets to a significant extent, and the div in question is regularly being used as a bucket to hold content including widgets, then I would highly recommend you have a look at dijit.layout.ContentPane. ContentPane is a widget primarily focused around the idea of a container that receives content, either directly or from a URL, which may or may not include widgets.
Right now you're probably doing something like this on each page change:
dojo.xhrGet({
url: 'something.html',
load: function(html) {
dojo.byId('main').innerHTML = html;
dojo.parser.parse(dojo.byId('main'));
}
error: function(error) { ... }
});
With a ContentPane, you could do the same thing like this:
cp.set('href', 'something.html'); //use attr instead of set if < dojo 1.5
With this, ContentPane will not only fetch that URL and hold its contents - it will also parse any widgets within it - and equally importantly, it will automatically destroy any existing widgets within itself before it replaces its content.
You can read more about it in the Dojo documentation:
http://dojotoolkit.org/reference-guide/dijit/layout/ContentPane.html
http://dojotoolkit.org/api/dijit/layout/ContentPane
Alternatively, if you don't feel like using a widget to hold your content, you can look for widgets in your div and destroy them yourself. Here's the easiest way to do it:
dojo.forEach(dijit.findWidgets(dojo.byId('main')), function(w) {
w.destroyRecursive();
});
dojo.query('selector').forEach(function(node){
dijit.byNode(node).destroyRecursive(true);
});
Basically, selecting the node... You can get the mapped as widget object by using dojo.byNode(node), and then destroyRecursive(true);
I solved a similar problem, simply deleting from registry using dijit.registry.remove('idName') after eliminating the content with destroyRecursive(false), before Reloading it.
if(typeof registry.byId("tableOfContents") != "undefined"){
registry.byId("tableOfContents").destroyRecursive(false);
dijit.registry.remove('tableOfContents');
}
If you have more than one widget to be destroyed on a page, the following solution works for me.
var widg = dijit.findWidgets(dojo.byId('root-id')); // root-id is top div id which encloses all widgets
$(widg).each(function(){
dijit.byId($(this).attr("id")).destroy(true);
});
I am writing a script that needs to add DOM elements to the page, at the place where the script is located (widget-like approach).
What is the best way to do this?
Here are the techniques I am considering:
Include an element with an id="Locator" right above the script. Issues:
I don't like the extra markup
If I reuse the widget in the page, several elements will have the same "Locator" id. I was thinking about adding a line in the script to remove the id once used, but still...
Add an id to the script. Issues:
even though it seems to work, the id attribute is not valid for the script element
same issue as above, several elements will have the same id if I reuse the script in the page.
Use getElementsByTagName("script") and pick the last element. This has worked for me so far, it just seems a little heavy and I am not sure if it is reliable (thinking about deferred scripts)
document.write: not elegant, but seems to do the job.
[Edit] Based on the reply from idealmachine, I am thinking about one more option:
Include in the script tag an attribute, for example goal="tabify".
Use getElementsByTagName("script") to get all the scripts.
Loop through the scripts and check the goal="tabify" attribute to find my script.
Remove the goal attribute in case there's another widget in the page.
[Edit] Another idea, also inspired by the replies so far:
Use getElementsByTagName("script") to get all the scripts.
Loop through the scripts and check innerHTML to find my script.
At the end of the script, remove the script tag in case there's another widget in the page.
Out of the box : document.currentScript (not supported by IE)
I've worked for OnlyWire which provides, as their main service, a widget to put on your site.
We use the var scripts = document.getElementsByTagName("script"); var thisScript = scripts[scripts.length - 1]; trick and it seems to work pretty well. Then we use thisScript.parentNode.insertBefore(ga, thisScript); to insert whatever we want before it, in the DOM tree.
I'm not sure I understand why you consider this a "heavy" solution... it doesn't involve iteration, it's a pure cross-browser solution which integrates perfectly.
This works with multiple copies of same code on page as well as with dynamically inserted code:
<script type="text/javascript" class="to-run">
(function(self){
if (self == window) {
var script = document.querySelector('script.to-run');
script.className = '';
Function(script.innerHTML).call(script);
} else {
// Do real stuff here. self refers to current script element.
console.log(1, self);
}
})(this);
</script>
Either document.write or picking the last script element will work for synchronously loaded scripts in the majority of web pages. However, there are some options I can think of that you did not consider to allow for async loading:
Adding a div with class="Locator" before the script. HTML classes has the advantage that duplicates are not invalid. Of course, to handle the multiple widget case, you will want to change the element's class name when done adding the HTML elements so you do not add them twice. (Note that it is also possible for an element to be a member of multiple classes; it is a space-separated list.)
Checking the src of each script element can ensure that tracking code (e.g. Google Analytics legacy tracking code) and other scripts loaded at the very end of the page will not prevent your script from working properly when async loading is used. Again, to handle the multiple widget case, you may need to remove the script elements when done with them (i.e. when the desired code has been added to the page).
One final comment I will make (although you may already be aware of this) is that when coding a widget, you need to declare all your variables using var and enclose all your code within: (JSLint can help check this)
(function(){
...
})();
This has been called a "self-executing function" and will ensure that variables used in your script do not interfere with the rest of the Web page.
Whether you drop a <script> tag in or a <div class="mywidget">, you're adding something to the markup. Personally, I prefer the latter as the script itself is only added once. Too many scripts in the page body can slow down the page load time.
But if you need to add the script tag where the widget is going to be, I don't see what's wrong with using document.write() to place a div.
I just found another method that seems to answer my question:
How to access parent Iframe from javascript
Embedding the script in an iframe allows to locate it anytime, as the script always keeps a reference to its own window.
I vote this the best approach, as it'll always work no matter how many times you add the script to the page (think widget). You're welcome to comment.
What pushed me to consider iframes in the first place was an experiment I did to build a Google gadget.
In many cases this work well (hud.js is the name of the scipt):
var jsscript = document.getElementsByTagName("script");
for (var i = 0; i < jsscript.length; i++) {
var pattern = /hud.js/i;
if ( pattern.test( jsscript[i].getAttribute("src") ) )
{
var parser = document.createElement('a');
parser.href = jsscript[i].getAttribute("src");
host = parser.host;
}
}
Also you can add individual script's name inside them.
either inside some js-script
dataset['my_prefix_name'] = 'someScriptName'
or inside HTML - in the <script> tag
data-my_prefix_name='someScriptName'
and next search appropriate one by looping over document.scripts array:
... function(){
for (var i = 0, n = document.scripts.length; i < n; i++) {
var prefix = document.scripts[i].dataset['my_prefix_name']
if (prefix == 'whatYouNeed')
return prefix
}
}
I haven't had access to internet explorer since forever, but this should work pretty much everywhere:
<script src="script.js"
data-count="30"
data-headline="My headline"
onload="uniqueFunctionName(this)"
defer
></script>
and inside script.js:
window.uniqueFunctionName = function (currentScript) {
var dataset = currentScript.dataset
console.log(dataset['count'])
console.log(dataset['headline'])
}