Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conditionally load advertising manually #35

Merged
merged 1 commit into from
Oct 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ The following data attributes are supported on the ad placement element:
This can only further reduce campaign types, not allow ones prohibited for the publisher.
This is useful when you want certain users to not get certain types of ads.

``data-ea-manual`` (optional)
Set to ``true`` if you want to :ref:`manually load ads <load manually>` at a specific future time for your app.
This is useful if you want to conditionally load advertising for some users but not others
or only load advertising when specific actions are performed.

Themes
------

Expand Down Expand Up @@ -311,6 +316,25 @@ You can show backup content with a code snippet like this:

.. warning:: You need to have ``Allow house campaigns`` disabled in the ads dashboard, otherwise we will always return a house ad. Go to :guilabel:`Settings > Control advertiser campaign types` to disable it.


.. _load manually:

Manually loading ads
--------------------

You can precisely determine when an ad will be loaded by setting the ``data-ea-manual`` attribute to ``true``.
This is useful if you want to conditionally show advertising or only show advertising when specific actions occur.

.. code:: html

<div data-ea-publisher="..." data-ea-manual="true"></div>
<script>
$(document).ready(() => {
ethicalads.load();
});
</script>


.. _signup:

Becoming a Publisher
Expand Down
43 changes: 39 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,23 @@ const SUPPORTS_MULTIPLE_PLACEMENTS = false;
* @param {Element} target - Target element
* @param {string} keywords - An optional | separated array of keywords
* @param {string} campaign_types - An optional | separated array of campaign types
* @param {boolean} load_manually - whether this placement will be loaded manually later
*/
export class Placement {
constructor(publisher, ad_type = "image", target, keywords, campaign_types) {
constructor(publisher, ad_type = "image", target, keywords, campaign_types, load_manually) {
this.publisher = publisher;
this.ad_type = ad_type;
this.target = target;

this.keywords = keywords || "";
this.campaign_types = campaign_types || "paid|community|house";

this.load_manually = load_manually;
}

/* Create a placement from an element
*
* Returns null if the placement is already loaded.
*
* @static
* @param {Element} element - Load placement and append to this Element
Expand All @@ -75,12 +80,20 @@ export class Placement {
const keywords = element.getAttribute(ATTR_PREFIX + "keywords");
const campaign_types = element.getAttribute(ATTR_PREFIX + "campaign-types");

const load_manually = element.getAttribute(ATTR_PREFIX + "manual") === "true";

// Add version to ad type to verison the HTML return
if (ad_type === "image" || ad_type === "text") {
ad_type += "-v" + AD_CLIENT_VERSION;
}

return new Placement(publisher, ad_type, element, keywords, campaign_types);
let classes = (element.className || "").split(" ");
if (classes.includes("loaded")) {
console.error("EthicalAd already loaded.");
return null;
}

return new Placement(publisher, ad_type, element, keywords, campaign_types, load_manually);
}

/* Transforms target element into a placement
Expand Down Expand Up @@ -169,9 +182,10 @@ export class Placement {

/* Find all placement DOM elements and hot load HTML as child nodes
*
* @param {boolean} force_load - load placements even if they are set to load manually
* @returns {Promise<[Placement]>} Resolves to a list of Placement instances
*/
export function load_placements() {
export function load_placements(force_load = false) {
// Find all elements matching required data binding attribute. We don't yet
// support multiple placements on the ad-server. For now, this could result in
// competing ad placements.
Expand All @@ -193,7 +207,12 @@ export function load_placements() {
return Promise.all(
elements.map((element) => {
const placement = Placement.from_element(element);
return placement.load();
if (placement && (force_load || !placement.load_manually)) {
return placement.load();
} else {
// This will be manually loaded later or has already been loaded
return null;
}
})
);
}
Expand Down Expand Up @@ -224,6 +243,17 @@ class EthicalAdsWarning extends Error {}
*/
export var wait;

/* Loading placements manually rather than the normal way
*
* <div data-ea-publisher="..." data-ea-manual="true"></div>
* <script>
* ethicalads.load();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this allow us to set keywords on the request? That seems important since we don't have that data prior?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could probably make it accept options but it doesn't currently. You can still attach the keywords to the div the normal way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but I thought the point of this PR was to use it on RTD, where we don't know the keywords until we get the API response?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My plan was to add/modify that DOM element after the API response.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll test this out and see how well it works. Maybe we do need additional options to load().

* </script>
*
* @type function
*/
export var load;

/* If importing this as a module, do not automatically process DOM and fetch the
* ad placement. Only do this if using the module directly, from a `script`
* element. This will allow for future extension and packaging as a module.
Expand Down Expand Up @@ -274,4 +304,9 @@ if (require.main !== module) {
});
});
});

load = () => {
console.log("Loading placements manually")
load_placements(true);
};
}