I've run into a particularly troubling issue when trying to attach external libraries which URL changes based on the translation of the site.
To sketch the situation:
There's an element on a website I'm working on which loads in an external Javascript file to display certain contents.
This element is only shown on a specific page rendered by a module.
The languages are noted by subdomain, for example: uk.example.com, de.example.com
The script should be loaded based on this subdomain, so: uk.example.com/script.js, de.example.com/script.js , The path will always be the same.
The problem I'm running into:
While attaching the Javascript using a HOOK_library_info_alter() the Javascript source URL gets cached, this means that the uk version of the script gets loaded in on de de version of the site. It's not possible to change this system, these scripts need to be loaded using different URLs for reasons I wont go in.
I've tried adding the script using a HOOK_page_attachments to put the script in the header with the correct subdomain, except it is impossible to determine if the script only gets loaded on that specific page, with that specific element (Using library_info_alter I'm able to check if the $extension is correct)
Is there any possible solution to this problem?
I'm sorry if it's worded problematic, my english isn't exactly amazing.
Here is the outline of a possible solution:
function yourmodule_page_attachments(array &$page) {
$current_path = \Drupal::service('path.current')->getPath();
$language = \Drupal::languageManager()->getCurrentLanguage()->getId();
if($current_path == '/node/123') {
if($language == 'en') {
$page['#attached']['library'][] = 'yourmodule/extlib-en';
}
}
}
You will need to change "yourmodule", "/node/123" and "en" to fit, obviously, and define "extlib-en" and other language-specific libraries in yourmodule.liraries.yml, as defined in Drupal 8 documentation.
Related
I'm working on site that uses GTM(Google Tag Manager).
GTM includes some script(tag) from a site that not allowed in my country. It causes an error in console and I want to stop loading this tag. I don't have access to GTM account so I should do it with js. This script is Custom HTML Tag because when I try below code it stops loading:
dataLayer = [{
'gtm.blacklist':['html']
}];
but it also stops loading other custom tags.
How can I stop loading certain custom tag programmatically?
I think it can be done. Looking into GTM code we can see it uses insertBefore function to add Script elements to website (for now, they can change this at any time). So in theory you can "add" some code to the native function and prevent loading of scripts from specific sources. For example, you can run following code before you load GTM:
Node.prototype.insertBefore = (function() {
var cached_function = Node.prototype.insertBefore;
return function(script) {
if(script && script.src.indexOf("www.somesource.com/script.js") !== -1){ //change to src you don't want to load on your page
return false; //don't add the script
}else{
var result = cached_function.apply(this, arguments); // use .apply() to call native function
return result;
}
};
})();
(code taken from: Adding code to a javascript function programmatically)
I didn't test this code, so I am not advising you to do it without proper testing or you might decide not to do it at all(before you do you might want to read: Why is extending native objects a bad practice?). I agree with Eike's answer but all I am saying is that it is possible to prevent loading of custom tags programmatically.
You cannot block a specific custom HTML programmatically. One reason is that it would be pointless - "custom HTML" means "arbitrary code executed in the context of your site", so the code could simply be put into another HTML tag and be run from there.
That you are not in control of the instance of GTM that runs in your site (which effectively means that you are not in control of your site) is not a use case that Google could cater for in any meaningful way (if you are in control of GTM then simply remove the tag).
If you mean that you want to disallow scripts of a certain origin then you might look into Content Security Policies (which will work no matter if the scripts runs from GTM or any other source). However CSPs are notoriously hard to implement (and while it is possible to implement them from within GTM this works only for limited testing, not for production use).
I'm trying to rationalize a website, and I have many links on it to the same document, so I want to create a JavaScript that return the URL of this document. This way, i could update the document and only have to change the URL in the function, not in all the occurrences of the link (it's a professional and internal website, with many links to official documents, that get updated often, out of my control, and each time i get to update links, i realize a while after that i forgot some, even by searching in all html files. the site is messy, was poorly written by many people, and that's why i'm trying to simplify)
My first idea was to use link, but everyone says it's a bad practice. i still don't see why, and I don't like to use the onclick as it doesn't work with middle click, and i want to let users decide how they open the doc.
Also, I want to use link to redirect to a specific page.
on top of this, what i tried so far is not working like I intend, so i would need some help, whether to come up with a better solution, or to make this work!
here is my js, with different versions:
function F_link_PDF() {
// i was pretty sure this would work
return "http://www.example.com/presentation.pdf" ;
}
function F_link_PDF_2() {
document.write("http://www.example.com/presentation.pdf");
}
function F_link_PDF_3() {
// i don't like this solution, as it doesn't open as user intended to
location.href = "http://www.example.com/presentation.pdf" ;
}
this example is for a pdf document, but i could also need this for html, doc, ppt...
and finally, i started with js because i'm used to, but I could also use other languages, php, asp, if someone says it's a better option
thanks in advance!
The hack way: Go about using JavaScript, however you run into potential issues with browsers not running it.
The better way: Use mod_rewrite / .htaccess to redirect previous (expired) requests to the new location of the resource. You could also use FallbackResource and provide a .php file that could provide the new resource based on criteria (you now have the power of PHP to decide where the Location header should go).
The best way1: Place those document references in a database table somewhere and reference them in the page using the table's current value. This creates a single place of "truth" and allows you to update the site from a global perspective. You could also, at a later date, provide search, tag, display a list, etc.
1 Not implying it's the abosolute best, but it is certainly a better way than updating hard-coded references.
A server side programming language like php is a better option.
Here's example code that helps:
<?php
$link="http://www.example.com/files/document.pdf";
if ($_GET['PAGE'] == "downloads")
{
?>
This is a download page where you can download our flyer.
<?php
echo "Download PDF";
}
if ($_GET['PAGE'] == "specials")
{
?>
This is our store specials page. check them out. a link to the flyer is below.
<?php
echo "Download PDF";
}
?>
The code isn't 100% perfect since some text needs adjusting but what it does is it takes a parameter PAGE and sees that it is "downloads" or "specials" and if it is, it loads the appropriate page and adds the link to the download file. If you try both pages, the link to the download is exactly the same.
If the above php script is saved as index.php, then you can call each page with:
index.php?PAGE=specials for the specials page
index.php?PAGE=downloads for the download page
Once that works, then you can add another "if" section for another page to create but the most important line in each section is the last line of...
echo "Download PDF";
...because it's taking a variable thats usable in every case in the script.
An advantage with using server side method is that people can view the site even with javascript disabled.
I have a website in php, that pass certain php variables to javascript variables, google crawled me, which generates errors and duplicate content. Is there any way to make the google crawler to ignore the declaration of these variables in javascript?
echo '<script language="javascript">var '.$item['Nombre'].'="'.$descripcion.'";</script>';
Sorry for my english,
Google crawling javascript code and considering it duplicate? I have never heard of this problem before. Some of my pages have inlined javascript (if the content is small), that means the same <script>...</script> on every page.
There are also cases where I output javascript variables more-or-less the same way you do. Google never marked it as "duplicate content".
Description from here:
Duplicate content generally refers to substantive blocks of content
within or across domains that either completely match other content or
are appreciably similar. Mostly, this is not deceptive in origin.
Examples of non-malicious duplicate content could include:
Discussion forums that can generate both regular and stripped-down pages targeted at mobile devices
Store items shown or linked via multiple distinct URLs
Printer-only versions of web pages
You can get this kind of errors if you have the same content on more than one of your pages, but google does not parse javascript as content. (Although you can never know for sure what google does or does not). The same way that google will not mark your <head> tag as duplicate, or there is no penalty for having the same layout (menu, footer, etc) on every page.
You can put that <script> tag in an <aside> tag just to be sure.
The HTML Element represents a section of a page that consists
of content that is tangentially related to the content around it,
which could be considered separate from that content. Such sections
are often represented as sidebars or as inserts. They often contain
side explanations, like a glossary definition; more loosely related
stuff, like advertisements; the biography of the author; or in
web-applications, profile information or related blog links.
This means that the content will be more or less ignored by google when indexing the page. It will not mark it as a duplicate since it could be a commertial.
Also drop the language="javascript" attribute from your script tags. I doubt that it would confuse google in any way, since that attribute is deprecated (use type instead) and nothing takes it into account nowadays. But if google bot does, the correct value would be text/javascript instead of simply javascript. It is possible that google does not recognise the value javascript and parses it as unknown type of text content.
The default type of the script is text/javascript, so it is safe to omit.
Above all I suspect that the problem is not the existence of JS variables, but some other thing like GET parameters in your URL. GET parameters can be dealt with by configuring URL Parameters correctly in Webmaster Tools.
Important: This is bad practice in most of the cases. If google notices that you serve different content to it's bot and considers it relevant, than your site can get penalties beyond measure.
I do recommend this php solution:
in PHP use this code:
if (!strpos($_SERVER[‘HTTP_USER_AGENT’],"Googlebot")) {
//echo the script
}
else{ //dont echo, does nothing. }
But if this don't work you can try adding this javascript code into your script tag:
if (!navigator.userAgent.contains('Googlebot')) {
//do the script
} else {
//does nothing
}
Ps: Here is a list of User-Agents http://www.useragentstring.com/pages/Crawlerlist/
Another (untested, speculative) approach that requires that you can write your own robots.txt file:
Move all your javascript code generation to another URL and include this as a javascript script in your page: <script type="text/javascript" src="/path/to/my/php/that/generates/js/variables.php"></script>
Add that URL to your robots.txt file (see Google answer)
User-Agent: Googlebot
Disallow: /path/to/my/php/that/generates/js/variables.php
You can Use following PHP code:
$crawlers = array(
'Google'=>'Google',
'MSN' => 'msnbot',
'Rambler'=>'Rambler',
'Yahoo'=> 'Yahoo',
'AbachoBOT'=> 'AbachoBOT',
'accoona'=> 'Accoona',
'AcoiRobot'=> 'AcoiRobot',
'ASPSeek'=> 'ASPSeek',
'CrocCrawler'=> 'CrocCrawler',
'Dumbot'=> 'Dumbot',
'FAST-WebCrawler'=> 'FAST-WebCrawler',
'GeonaBot'=> 'GeonaBot',
'Gigabot'=> 'Gigabot',
'Lycos spider'=> 'Lycos',
'MSRBOT'=> 'MSRBOT',
'Altavista robot'=> 'Scooter',
'AltaVista robot'=> 'Altavista',
'ID-Search Bot'=> 'IDBot',
'eStyle Bot'=> 'eStyle',
'Scrubby robot'=> 'Scrubby',
);
function crawlerDetect($USER_AGENT)
{
// to get crawlers string used in function uncomment it
// it is better to save it in string than use implode every time
// global $crawlers
// $crawlers_agents = implode('|',$crawlers);
$crawlers_agents = 'Google|msnbot|Rambler|Yahoo|AbachoBOT|accoona|AcioRobot|ASPSeek|CocoCrawler|Dumbot|FAST-WebCrawler|GeonaBot|Gigabot|Lycos|MSRBOT|Scooter|AltaVista|IDBot|eStyle|Scrubby';
if ( strpos($crawlers_agents , $USER_AGENT) === false )
return false;
// crawler detected
// you can use it to return its name
/*
else {
1,1 Top
return array_search($USER_AGENT, $crawlers);
}
*/
}
Using above method you can check Request is coming from crawler or not.
I have a webpage that works and all is swell. It is coded using mostly good practises of external css files and minimal inline styles/code. All is well.
Now however, I want to send that page via HTML text only, such as in an email. So there should be no external references to external sites at all. Meaning I now must move my beautiful external references, internally.
I am thinking I can write a javascript function that finds every class of an object, removes it from that class, then gives that object inline "style" attributes equal to what the class has.
But I was wondering if anyone else has other suggestions.
The end goal is to get a wall of text, that when pasted in a non-internet connected browser with no cache or anything, will display exactly what I have on the screen of my "normal operations" page.
There is a perl CPAN module for this:
CSS::Inliner
you can also find the source on github:
https://github.com/kamelkev/CSS-Inliner
Quick question, I have some scripts that only need to be run on some pages and some only on a certain page, would it be best to include the script at the bottom of the actual page with script tags or do something like in my js inlcude;
var pageURL = window.location.href;
if (pageURL == 'http://example.com') {
// run code
}
Which would be better and faster?
The best is to include the script only on pages that need it. Also in terms of maintenance your script is more independant from the pages that are using it. Putting those ifs in your script makes it tightly coupled to the structure of your site and if you decide to rename some page it will no longer work.
I can recommend you to use an asynchrounous resource loader, LAB.js for example. Then you could build a dependencies list, for instance:
var MYAPP = MYAPP || {};
/*
* Bunches of scripts
* to load together
*/
MYAPP.bunches = {
defaults: ["libs/jquery-1.6.2.min.js"],
cart: ["plugins/jquery.tmpl.min.js",
"libs/knockout-1.2.1.min.js",
"scripts/shopping-cart.js"],
signup: ["libs/knockout-1.2.1.min.js",
"scripts/validator.js"]
/*
... etc
*/
};
/*
* Loading default libraries
*/
$LAB.script(MYAPP.defaults);
if (typeof MYAPP.require !== 'undefined') {
$LAB.script(MYAPP.dependencies[MYAPP.require]);
}
and in the end of your page you could write:
<script type="text/javascript">
var MYAPP = MYAPP || {};
MYAPP.require = "cart";
</script>
<script type="text/javascript" src='js/libs/LAB.min.js'></script>
<script type="text/javascript" src='js/dependencies.js'></script>
By the way, a question to everyone, is it a good idea to do so?
In so far as possible only include the scripts on the pages that requirement. That said, if you're delivering content via AJAX that can be hard to do, since the script might already be loaded and reloading could cause problems. Of course you can deliver code in a script block (as opposed to referencing an external js file), in code delivered via AJAX.
In cases where you need to load scripts (say via a master page) for all pages, but that only apply to certain pages, take advantage of the fact that jQuery understands and deals well with selectors that don't match any elements. You can also use live handlers along with very specific selectors to allow scripts loaded at page load time to work with elements added dynamically later.
Note: if you use scripts loaded via content distribution network, you'll find that they are often cached locally in the browser anyway and don't really hurt your page load time. The same is true with scripts on your own site, if they've already been loaded once.
You have two competing things to optimize for, page load time over the network and page initialization time.
You can minimize your page load time over the network by taking maximum advantage of browser caching so that JS files don't have to be loaded over the network. To do this, you want as much javascript code for your site in on or two larger and fully minimized JS files. To do this, you should put JS for multiple different pages in one common JS file. It will vary from site to site whether the JS for all pages should be ine one or two larger JS files or whether you group it into a small number of common JS files that are each targeted at part of your site. But, the general idea is that you want to combine the JS code from different pages into a common JS file that can be most effectively cached.
You can minimize your page initialization time by only calling initialization code that actually needs to execute on the particular page that is being displayed. There are several different ways to approach this. I agree with the other callers that you do not want to be looking at URLs to decide which code to execute because this ties your code to the URL structure which is better to avoid. If your code has a manageable number of different types of pages, then I'd recommend identifying each of those page types with a unique class name on the body tag. You can then have your initialization code look for the appropriate class on the body tag and branch to the appropriate initialization code based on that. I've even seen it done where you find a class name with a particular common prefix, parse out the non-common part of the name and call an initialization function by that name. This allows you to give a page a specific set of behaviors by only adding a classname to the body tag. The code remains very separate from the actual page.
the less general purpose way of doing this is to keep all the code in the one or two common JS files, but to add the appropriate initialization call to each specific page's HTML. So, the JS code that does the initialization code lives in the common JS files and thus is maximally cached, but the calling of the appropriate initialization code for that page is embedded inline in each specific page. This minimizes the execution time of the initialization, but still lets you use maximal caching. It's slightly less generic than the class name technique mentioned earlier, but some may like the more direct calling technique.
Include scripts at bottom of pages that need it only.
The YSlow add-on is the best solution to know why your website is slow.
There are many issues which could be the reason for slowness.
Combining many jQuery to one could help you increasing your performance.
Also you can put the script at the bottom of your page and CSS at top.
Its basically up to you and depends on what the code is.
Generally with small things I will slip it into the bottom of the page. (I'm talking minor ui things that relate only to that page).
If you're doing the location ref testing for more than a couple pages it probably means you're doing something wrong.
You might want to take a look at one of these:
http://en.wikipedia.org/wiki/Unobtrusive_JavaScript
http://2tbsp.com/node/91
And as for which is faster it's wildly negligible, pick what is easier for you to maintain.